Hello Curtis,
> First let me explain what I need to do. I need to configure an
> "asymmetric view frustum". I need to place 3 monitors next to each
> other, aligned along a flat plane. The view drawn in each monitor needs
> to be projected on that same flat plane. I cannot just set a view
> offset for the side channels because the view won't come out right. If
> I simply rotate the view offset and take a symmetric view frustum, the
> plane of projection will be perpendicular to the viewer in each
> channel. That won't work for this particular application and it's not
> what I need. I need to configure an actual asymmetric view frustum for
> each side channel. If someone thinks they can help me, but is confused
> by my description, I'm happy to explain this further. Think about this
I hope the attached camera class might help you - I implemented support for
off-center and tiled projections for a similar project (powerwall
rendering).
The parts that probably interest you are in update_frustum() and the
parameters set via the setTiling(...) method.
hope to help,
Manuel
/***************************************************************************
Camera.cpp - description
-------------------
begin : Thu Apr 18 2002
written by : Manuel Massing
email : [EMAIL PROTECTED]
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "Camera.h"
Camera::Camera()
{
n = 1.0f;
f = 4000.f;
fov = 45.f;
eye_separation = 0.f;
focal_length = 2000.f;
resx = 1; resy = 1;
llx = lly = 0;
aspect = 4.f/3.f;;
tile_t = tile_r = 1.f;
tile_b = tile_l = 0.f;
frustum_dirty = orientation_dirty = obj2clip_dirty = true;
}
void Camera::update_frustum() const
{
if (frustum_dirty) {
// Calculate the symmetric frustum extent at focal distance.
float frustum_top = focal_length*tanf(fov*M_PI/360.f);
float frustum_left = -frustum_top*aspect*((tile_t-tile_b)/(tile_r-tile_l));
// Adapt frustum to desired viewport region.
t = (2.f*tile_t - 1.f)*frustum_top;
b = (2.f*tile_b - 1.f)*frustum_top;
l = (1.f - 2.f*tile_l)*(frustum_left - eye_separation);
r = (1.f - 2.f*tile_r)*(frustum_left - eye_separation);
float rescale = n/focal_length;
t*= rescale;
b*= rescale;
l*= rescale;
r*= rescale;
local_clipplane[cpNear] = Plane(Vector3d(0.f, 0.f, -1.f), n); // near
local_clipplane[cpFar] = Plane(Vector3d(0.f, 0.f, 1.f), -f); // far
Vector3d cpn(n, 0, l);
cpn*=1.f/cpn.length();
local_clipplane[cpLeft] = Plane(cpn, 0); // left
cpn = !Vector3d(-n, 0, -r);
local_clipplane[cpRight] = Plane(cpn, 0); // right
cpn = !Vector3d(0, -n, -t);
local_clipplane[cpTop] = Plane(cpn, 0); // top
cpn = !Vector3d(0, n, b);
local_clipplane[cpBottom] = Plane(cpn, 0); // bottom
frustum = Matrix4x4( 2.f*n/(r - l), 0.f, (r + l)/(r - l), 0.f,
0.f, 2.f*n/(t - b), (t + b)/(t - b), 0.f,
0.f, 0.f, -(f + n)/(f - n), -2.f*f*n/(f - n),
0.f, 0.f, -1.f, 0.f );
#ifdef LOGGING
if (hasLogfile())
get_logfile().setItem("fov", fov);
#endif
frustum_dirty = false;
obj2clip_dirty = true;
}
}
void Camera::update_modelview() const
{
if (orientation_dirty) {
if (vp == vpLookAt) {
Vector3d xAxis, zAxis, tmp_up = up;
zAxis = !(lookat - pos);
xAxis = !cross(zAxis, tmp_up);
tmp_up = !cross(xAxis, zAxis);
Matrix3x3 rotation = Matrix3x3(xAxis, tmp_up, -zAxis).transpose();
modelview = Matrix4x4(rotation, rotation*(-pos - eye_separation*xAxis));
}
else if (vp == vpTargetRoll) {
Vector3d xAxis, zAxis, tmp_up = up;
zAxis = !(lookat - pos);
// Make sure we are not looking down the y-axis
if (!zAxis.x && !zAxis.z)
tmp_up = Vector3d(-zAxis.y, 0.f, 0.f);
else
tmp_up = Vector3d(0.f, 1.f, 0.f);
xAxis = !cross(zAxis, tmp_up);
tmp_up = !cross(xAxis, zAxis);
Matrix3x3 rotation = Matrix3x3(xAxis, tmp_up, -zAxis).transpose();
Matrix3x3 camroll;
camroll.setRotateZ(roll);
rotation = camroll * rotation;
modelview = Matrix4x4(rotation, rotation*(-pos - eye_separation*xAxis));
}
else if (vp == vpOrientationMatrix) {
Matrix3x3 rotate = orientation.transpose();
modelview = Matrix4x4(rotate, rotate*(-pos - eye_separation*orientation(0)));
}
obj2clip_dirty = true;
orientation_dirty = false;
}
}
void Camera::update() const
{
update_frustum();
update_modelview();
if (obj2clip_dirty) {
for (int i = 0; i < 6; i++)
world_clipplane[i] = modelview.affine_inverse()*local_clipplane[i];
obj2clip = modelview*frustum;
obj2clip_dirty = false;
}
}
void Camera::activate() const
{
update();
//should compare to current viewport (static)
glViewport(llx, lly, resx, resy);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf((GLfloat *)&frustum.mat);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf((GLfloat *)&modelview.mat);
}
void Camera::setResolution(int resX, int resY, int llX, int llY, float PixelAspect)
{
resx = resX;
resy = resY;
llx = llX;
lly = lly;
aspect = (float)resX*PixelAspect/(float)resY;
}
void Camera::setDistanceRange(float Near, float Far)
{
frustum_dirty = true;
n = Near;
f = Far;
}
void Camera::getDistanceRange(float &Near, float &Far) const
{
Near = n;
Far = f;
}
void Camera::setFoV(float FoV)
{
frustum_dirty = true;
fov = FoV;
}
void Camera::setSeparation(float Separation)
{
if (fabs(eye_separation - Separation) > 0.f) {
eye_separation = Separation;
frustum_dirty = true;
orientation_dirty = true;
}
}
float Camera::getSeparation() const
{
return eye_separation;
}
void Camera::setFocalLength(float FocalLength)
{
if (focal_length != FocalLength) {
focal_length = FocalLength;
if (eye_separation > 0.f)
frustum_dirty = true;
}
}
float Camera::getFocalLength() const
{
return focal_length;
}
// Set the camera tiling,
void Camera::setTiling(float tileL, float tileR, float tileT, float tileB)
{
assert((0.f <= tileL) && (1.f >= tileL));
assert((0.f <= tileR) && (1.f >= tileR));
assert((0.f <= tileT) && (1.f >= tileT));
assert((0.f <= tileB) && (1.f >= tileB));
assert(tileL < tileR);
assert(tileB < tileT);
if ((tile_l != tileL) || (tile_r != tileR) ||(tile_t != tileT) || (tile_b != tileB)) {
tile_l = tileL;
tile_r = tileR;
tile_t = tileT;
tile_b = tileB;
frustum_dirty = true;
}
}
void Camera::setPosition(const Vector3d &Pos)
{
orientation_dirty = true;
pos = Pos;
}
void Camera::setTargetRoll(const Vector3d &Pos, const Vector3d &Target, float Roll)
{
orientation_dirty = true; vp = vpTargetRoll;
pos = Pos;
lookat = Target;
roll = Roll;
}
void Camera::setLookAt(const Vector3d &Pos, const Vector3d &LookAt, const Vector3d &Up)
{
orientation_dirty = true; vp = vpLookAt;
pos = Pos;
lookat = LookAt;
up = Up;
}
void Camera::setOrientation(const Matrix3x3 &Orientation)
{
orientation_dirty = true; vp = vpOrientationMatrix;
orientation = Orientation;
}
Matrix3x3 Camera::getOrientation() const
{
update();
return modelview.get3x3SubMatrix().transpose();
}
float Camera::getFoV() const
{
return fov;
}
void Camera::getResolution(int &resX, int &resY) const
{
resX = resx;
resY = resy;
}
// returns projected size in pixels of a unit length element along
// the x and y direction at distance "distance" from the image plane.
void Camera::getPixelProjection(float &pixelsX, float &pixelsY, float distance) const
{
distance*= 2.f*tanf(fov*M_PI/360.f);
pixelsY = (float)resy/distance;
pixelsX = (float)resx/(distance*aspect);
}
const Vector3d &Camera::getPosition() const
{
return pos;
}
const Vector3d &Camera::getLookAt() const
{
return lookat;
}
Vector3d Camera::getRightVector() const
{
update_modelview();
return Vector3d(modelview.mat[0], modelview.mat[4], modelview.mat[8]);
}
Vector3d Camera::getUpVector() const
{
update_modelview();
return Vector3d(modelview.mat[1], modelview.mat[5], modelview.mat[9]);
}
Vector3d Camera::getForwardVector() const
{
update_modelview();
return -Vector3d(modelview.mat[2], modelview.mat[6], modelview.mat[10]);
}
const Plane &Camera::getClipPlane(eClipPlane cp) const
{
uint i = (uint)cp;
if (i > 5) i = 0;
update();
return world_clipplane[i];
}
Vector3d Camera::unprojectFromDevice(const Vector3d &win) const
{
update();
Vector4d w((win.x + llx)*2.f/resx - 1.f,
(win.y + lly)*2.f/resy - 1.f,
win.z,
1.f);
//cout<<"unproject clip space: ";
//w.print();
w = obj2clip.inverse()*w;
return Vector3d(w.x/w.w, w.y/w.w, w.z/w.w);
}
void Camera::draw() const
{
update();
Matrix4x4 cam2world = modelview.affine_inverse();
float fn_ratio = 250.f; // f/n;
Vector3d lt = cam2world*Vector3d(l, t, -n);
Vector3d lt_f = cam2world*(Vector3d(l, t, -n)*fn_ratio);
Vector3d rt = cam2world*Vector3d(r, t, -n);
Vector3d rt_f = cam2world*(Vector3d(r, t, -n)*fn_ratio);
Vector3d lb = cam2world*Vector3d(l, b, -n);
Vector3d lb_f = cam2world*(Vector3d(l, b, -n)*fn_ratio);
Vector3d rb = cam2world*Vector3d(r, b, -n);
Vector3d rb_f = cam2world*(Vector3d(r, b, -n)*fn_ratio);
glColor3f(1.0f, 1.f, 1.f);
glBegin(GL_LINES);
glVertex3fv((GLfloat *)&pos);
glVertex3fv((GLfloat *)<);
glVertex3fv((GLfloat *)&pos);
glVertex3fv((GLfloat *)&rt);
glVertex3fv((GLfloat *)&pos);
glVertex3fv((GLfloat *)&lb);
glVertex3fv((GLfloat *)&pos);
glVertex3fv((GLfloat *)&rb);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3fv((GLfloat *)<);
glVertex3fv((GLfloat *)&rt);
glVertex3fv((GLfloat *)&rb);
glVertex3fv((GLfloat *)&lb);
glEnd();
glColor3f(1.f, 1.f, 1.f);
glBegin(GL_LINES);
glVertex3fv((GLfloat *)<);
glVertex3fv((GLfloat *)<_f);
glVertex3fv((GLfloat *)&rt);
glVertex3fv((GLfloat *)&rt_f);
glVertex3fv((GLfloat *)&lb);
glVertex3fv((GLfloat *)&lb_f);
glVertex3fv((GLfloat *)&rb);
glVertex3fv((GLfloat *)&rb_f);
glEnd();
/* glBegin(GL_LINE_LOOP);
glVertex3fv((GLfloat *)<_f);
glVertex3fv((GLfloat *)&rt_f);
glVertex3fv((GLfloat *)&rb_f);
glVertex3fv((GLfloat *)&lb_f);
glEnd();*/
}
/***************************************************************************
Camera.h - description
-------------------
begin : Sun Apr 7 2002
written by : Manuel Massing
email : [EMAIL PROTECTED]
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef CAMERA_H
#define CAMERA_H
#include "Global.h"
#include <cmath>
#include "glew.h"
#include "Base.h"
#include "Matrix.h"
#include "Plane.h"
#include "Vector.h"
/**
*\brief An OpenGL camera class.
*
*\class Camera
* This class is responsible for building of projection and model-view matrices
* from different parametrisations, for the specification of clipping planes and for updating
* OpenGL projection & model-view matrices accordingly.
*/
class Camera : protected Base {
protected:
/**
*\enum eViewParametrisations Indicates employed viewing parameters.
*/
enum eViewParametrisations {
vpLookAt,
vpTargetRoll,
vpOrientationMatrix
};
mutable Plane local_clipplane[6];
mutable Plane world_clipplane[6];
/**
* The Modelview Matrix can be specified in several independent ways,
* hence we select the appropriate ones via the enumeration variable "vp".
*/
eViewParametrisations vp;
Vector3d pos, lookat, up;
Matrix3x3 orientation;
// (Unfortunately, C++ doesn't allow for classes in unions)
union {
float roll;
};
mutable Matrix4x4 obj2clip, modelview, frustum;
/* The projection matrix is parametrized by horizontal and vertical resolution and the
coordinates of the lower left corner of the viewport */
int resx, resy;
int llx, lly;
/* The camera frustum is parametrized by the vertical field of view, the distance of
near & far clipping plane, the display aspect-ratio, and occular distance
(relative to the camera position). */
float fov;
float aspect;
float n, f;
float eye_separation;
float focal_length;
mutable float l, r, b, t;
//! Indicates wether to use the camera for a sector of a tiled display.
bool tiled_display;
float tile_l, tile_r, tile_t, tile_b; // Percent of the imaging plane, left = 0, right = 1
/**
* "Dirty flags" specify changed parameters to allow for deferred matrix calculations
*/
mutable bool obj2clip_dirty, frustum_dirty, orientation_dirty;
/**
* Updates the projection matrix and clipping planes according to field of view and aspect ratio.
*/
void update_frustum() const;
/**
* Updates the camera model-view matrix according to the chosen paramterisation.
*/
void update_modelview() const;
/**
* Updates all camera matrices and the clipping planes if viewing parameters have changed.
*/
void update() const;
public:
enum eClipPlane {
cpLeft = 0,
cpRight = 1,
cpBottom = 2,
cpTop = 3,
cpNear = 4,
cpFar = 5
};
enum eClipFlags {
cfLeft = 1<<cpLeft,
cfRight = 1<<cpRight,
cfBottom = 1<<cpBottom,
cfTop = 1<<cpTop,
cfNear = 1<<cpNear,
cfFar = 1<<cpFar
};
Camera();
/**
* Updates OpenGL projection and model-view matrix according to current viewing parameters.
*/
void activate() const;
/**
* Sets the resolution of the destination display
*/
void setResolution(int resX, int resY, int llX = 0, int llY = 0, float PixelAspect = 1.0f);
/**
* Sets the distance between eyepoint and near and far clipping planes.
*/
void setDistanceRange(float Near, float Far);
/**
* Sets the vertical field-of-view.
* \param FoV The field-of-view angle in degrees.
*/
void setFoV(float FoV);
/**
* Set the distance of the eye relative to the center of projection on the imaging plane.
*/
// void setEyeDistance(float eyeDistanceH, int eyeDistanceV);
/**
* Indicate the viewport sector when rendering part of a tiled display, e.g. on a PowerWall.
*/
void setTiling(float tileL = 0.f, float tileR = 1.f, float tileT = 0.f, float tileB = 1.f);
/**
* Sets the camera position, in world space coordinates.
*/
void setPosition(const Vector3d &Pos);
/**
* Specify camera orientation using position and target, and a roll angle.
*\param Pos Camera position in world space coordinates.
*\param Target Target of the camera in world space coordinates.
*\param Roll The amount of roll applied to the camera, in degrees.
*/
void setTargetRoll(const Vector3d &Pos, const Vector3d &Target, float Roll);
/**
* Specify camera orientation using position, lookat and the up vector.
*\param Pos Camera position in world space coordinates.
*\param LookAt Target of the camera in world space coordinates.
*\param Up An estimation of the up (y-axis) vector, which defines the camera roll.
*/
void setLookAt(const Vector3d &Pos, const Vector3d &LookAt, const Vector3d &Up);
void setOrientation(const Matrix3x3 &Orientation);
Matrix3x3 getOrientation() const;
void setSeparation(float Separation);
float getSeparation() const;
void setFocalLength(float FocalLength);
float getFocalLength() const;
float getFoV() const;
void getResolution(int &resX, int &resY) const;
/**
* \returns the horizontal and vertical projected size of an unit length square
* parallel to, and at distance "distance" from the imaging plane.
*/
void getPixelProjection(float &pixelsX, float &pixelsY, float distance = 1.0f) const;
Vector3d projectFromWorldspace(Vector3d &wpos) const
{
return Vector3d(0, 0, 0);
}
/**
*\returns the current camera position.
*/
const Vector3d &getPosition() const;
const Vector3d &getLookAt() const;
Vector3d getRightVector() const;
Vector3d getUpVector() const;
Vector3d getForwardVector() const;
const Plane &getClipPlane(eClipPlane cp) const;
void getDistanceRange(float &near, float &far) const;
/**
* Transforms the given vector into the canonical view frustum [origo = center of projection]
*/
Vector3d projectToScreen(const Vector3d &v) const
{
update();
Vector4d s = obj2clip*Vector4d(v, 1.0f);
float fac = 1.f/s.w;
return Vector3d(s.x*fac, s.y*fac, s.z*fac);
}
/**
* Transforms the given vector into window coordinates [origo = lower left corner]
*/
Vector3d projectToWindow(const Vector3d &v) const
{
update();
Vector4d s = obj2clip*Vector4d(v, 1.0f);
float fac = 0.5f/s.w;
return Vector3d(s.x*fac + 0.5f, s.y*fac + 0.5f, fabs(f-n)*s.z/s.w + (n+f/2.f));
}
/**
* Transforms the given vector from device coordinates [origo = lower left corner]
* to world space coordinates.
* \note that the z value is given in depth buffer units, not in normalized device
* coordinates!
*/
Vector3d unprojectFromDevice(const Vector3d &win) const;
/**
* Projects the given vector and converts it into 2d device coordinates [origo = lower left corner]
*/
Vector3d projectToDevice(const Vector3d &v) const
{
update();
Vector4d s = obj2clip*Vector4d(v, 1.0f);
float fac = 0.5f/s.w;
return Vector3d(llx + s.x*fac*(float)resx + resx*0.5f, lly + s.y*fac*(float)resy + resy*0.5f, s.z/s.w);
}
void draw() const;
int getClippingFlags(const Vector3d &v) const
{
update();
// Transform the vertex to clip-coordinates
Vector4d clip_coord = obj2clip*Vector4d(v, 1.0f);
int c = 0;
if (clip_coord.x < -clip_coord.w)
c|= cfLeft;
if (clip_coord.x > clip_coord.w)
c|= cfRight;
if (clip_coord.y < -clip_coord.w)
c|= cfBottom;
if (clip_coord.y > clip_coord.w)
c|= cfTop;
if (clip_coord.z < -clip_coord.w)
c|= cfNear;
if (clip_coord.z > clip_coord.w)
c|= cfFar;
return c;
}
};
#endif
_______________________________________________
Flightgear-devel mailing list
[email protected]
http://mail.flightgear.org/mailman/listinfo/flightgear-devel
2f585eeea02e2c79d7b1d8c4963bae2d