Merge pull request #3741 from ey6es/master

Use 3D geometry (cones, spheres) for rendering point and spot lights for deferred shading, exiting the fragment shaders early on depth fail.
This commit is contained in:
Philip Rosedale 2014-11-05 07:17:22 -08:00
commit 7e36c51edc
8 changed files with 202 additions and 68 deletions

View file

@ -11,50 +11,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the radius (hard cutoff) of the light effect
uniform float radius;
void main(void) {
// find the right "right" direction
vec3 firstRightCandidate = cross(gl_LightSource[1].spotDirection, vec3(0.0, 1.0, 0.0));
vec3 secondRightCandidate = cross(gl_LightSource[1].spotDirection, vec3(1.0, 0.0, 0.0));
vec3 right = mix(firstRightCandidate, secondRightCandidate, step(length(firstRightCandidate), length(secondRightCandidate)));
right = normalize(right);
// and the "up"
vec3 up = cross(right, gl_LightSource[1].spotDirection);
// and the "back," which depends on whether this is a spot light
vec3 back = -gl_LightSource[1].spotDirection * step(gl_LightSource[1].spotCosCutoff, 0.0);
// find the eight corners of the bounds
vec4 c0 = gl_ProjectionMatrix * vec4(gl_LightSource[1].position.xyz +
radius * (-up - right + back), 1.0);
vec4 c1 = gl_ProjectionMatrix * vec4(gl_LightSource[1].position.xyz +
radius * (-up + right + back), 1.0);
vec4 c2 = gl_ProjectionMatrix * vec4(gl_LightSource[1].position.xyz +
radius * (up - right + back), 1.0);
vec4 c3 = gl_ProjectionMatrix * vec4(gl_LightSource[1].position.xyz +
radius * (up + right + back), 1.0);
vec4 c4 = gl_ProjectionMatrix * vec4(gl_LightSource[1].position.xyz +
radius * (-up - right + gl_LightSource[1].spotDirection), 1.0);
vec4 c5 = gl_ProjectionMatrix * vec4(gl_LightSource[1].position.xyz +
radius * (-up + right + gl_LightSource[1].spotDirection), 1.0);
vec4 c6 = gl_ProjectionMatrix * vec4(gl_LightSource[1].position.xyz +
radius * (up - right + gl_LightSource[1].spotDirection), 1.0);
vec4 c7 = gl_ProjectionMatrix * vec4(gl_LightSource[1].position.xyz +
radius * (up + right + gl_LightSource[1].spotDirection), 1.0);
// find their projected extents
vec2 extents = max(
max(max(gl_Vertex.xy * (c0.xy / max(c0.w, 0.001)), gl_Vertex.xy * (c1.xy / max(c1.w, 0.001))),
max(gl_Vertex.xy * (c2.xy / max(c2.w, 0.001)), gl_Vertex.xy * (c3.xy / max(c3.w, 0.001)))),
max(max(gl_Vertex.xy * (c4.xy / max(c4.w, 0.001)), gl_Vertex.xy * (c5.xy / max(c5.w, 0.001))),
max(gl_Vertex.xy * (c6.xy / max(c6.w, 0.001)), gl_Vertex.xy * (c7.xy / max(c7.w, 0.001)))));
// make sure they don't extend beyond the screen
extents = min(extents, vec2(1.0, 1.0));
gl_Position = vec4(gl_Vertex.xy * extents, 0.0, 1.0);
gl_TexCoord[0] = vec4(dot(gl_Position, gl_ObjectPlaneS[3]), dot(gl_Position, gl_ObjectPlaneT[3]), 0.0, 1.0);
gl_Position = ftransform();
vec4 projected = gl_Position / gl_Position.w;
gl_TexCoord[0] = vec4(dot(projected, gl_ObjectPlaneS[3]), dot(projected, gl_ObjectPlaneT[3]), 0.0, 1.0);
}

View file

@ -39,8 +39,13 @@ uniform vec2 depthTexCoordScale;
uniform float radius;
void main(void) {
// get the depth and exit early if it doesn't pass the test
float depth = texture2D(depthMap, gl_TexCoord[0].st).r;
if (depth < gl_FragCoord.z) {
discard;
}
// compute the view space position using the depth
float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0);
float z = near / (depth * depthScale - 1.0);
vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0);
// get the normal from the map

View file

@ -39,8 +39,13 @@ uniform vec2 depthTexCoordScale;
uniform float radius;
void main(void) {
// get the depth and exit early if it doesn't pass the test
float depth = texture2D(depthMap, gl_TexCoord[0].st).r;
if (depth < gl_FragCoord.z) {
discard;
}
// compute the view space position using the depth
float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0);
float z = near / (depth * depthScale - 1.0);
vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0);
// get the normal from the map

View file

@ -126,6 +126,7 @@ void OculusManager::connect() {
if (!_camera) {
_camera = new Camera;
configureCamera(*_camera, 0, 0); // no need to use screen dimensions; they're ignored
}
if (!_programInitialized) {
@ -420,7 +421,7 @@ void OculusManager::endFrameTiming() {
//Sets the camera FoV and aspect ratio
void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) {
#ifdef HAVE_LIBOVR
camera.setAspectRatio((float)_renderTargetSize.w / _renderTargetSize.h);
camera.setAspectRatio(_renderTargetSize.w * 0.5f / _renderTargetSize.h);
camera.setFieldOfView(atan(_eyeFov[0].UpTan) * DEGREES_PER_RADIAN * 2.0f);
#endif
}

View file

@ -73,6 +73,12 @@ void DeferredLightingEffect::renderWireCube(float size) {
releaseSimpleProgram();
}
void DeferredLightingEffect::renderSolidCone(float base, float height, int slices, int stacks) {
bindSimpleProgram();
Application::getInstance()->getGeometryCache()->renderCone(base, height, slices, stacks);
releaseSimpleProgram();
}
void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& ambient,
const glm::vec3& diffuse, const glm::vec3& specular, float constantAttenuation,
float linearAttenuation, float quadraticAttenuation) {
@ -216,11 +222,18 @@ void DeferredLightingEffect::render() {
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_CULL_FACE);
glm::vec4 sCoefficients(sWidth / 2.0f, 0.0f, 0.0f, sMin + sWidth / 2.0f);
glm::vec4 tCoefficients(0.0f, tHeight / 2.0f, 0.0f, tMin + tHeight / 2.0f);
glTexGenfv(GL_S, GL_OBJECT_PLANE, (const GLfloat*)&sCoefficients);
glTexGenfv(GL_T, GL_OBJECT_PLANE, (const GLfloat*)&tCoefficients);
// enlarge the scales slightly to account for tesselation
const float SCALE_EXPANSION = 0.1f;
const glm::vec3& eyePoint = Application::getInstance()->getDisplayViewFrustum()->getPosition();
if (!_pointLights.isEmpty()) {
_pointLight.bind();
_pointLight.setUniformValue(_pointLightLocations.nearLocation, nearVal);
@ -238,7 +251,28 @@ void DeferredLightingEffect::render() {
glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, light.linearAttenuation);
glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation);
renderFullscreenQuad();
glPushMatrix();
float expandedRadius = light.radius * (1.0f + SCALE_EXPANSION);
if (glm::distance(eyePoint, glm::vec3(light.position)) < expandedRadius) {
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -1.0f);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
renderFullscreenQuad();
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
} else {
glTranslatef(light.position.x, light.position.y, light.position.z);
Application::getInstance()->getGeometryCache()->renderSphere(expandedRadius, 64, 64);
}
glPopMatrix();
}
_pointLights.clear();
@ -265,7 +299,34 @@ void DeferredLightingEffect::render() {
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, light.exponent);
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, glm::degrees(light.cutoff));
renderFullscreenQuad();
glPushMatrix();
float expandedRadius = light.radius * (1.0f + SCALE_EXPANSION);
float edgeRadius = expandedRadius / glm::cos(light.cutoff);
if (glm::distance(eyePoint, glm::vec3(light.position)) < edgeRadius) {
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -1.0f);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
renderFullscreenQuad();
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
} else {
glTranslatef(light.position.x, light.position.y, light.position.z);
glm::quat spotRotation = rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), light.direction);
glm::vec3 axis = glm::axis(spotRotation);
glRotatef(glm::degrees(glm::angle(spotRotation)), axis.x, axis.y, axis.z);
glTranslatef(0.0f, 0.0f, -light.radius * (1.0f + SCALE_EXPANSION * 0.5f));
Application::getInstance()->getGeometryCache()->renderCone(expandedRadius * glm::tan(light.cutoff),
expandedRadius, 64, 32);
}
glPopMatrix();
}
_spotLights.clear();
@ -285,6 +346,8 @@ void DeferredLightingEffect::render() {
freeFBO->release();
glDisable(GL_CULL_FACE);
// now transfer the lit region to the primary fbo
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
glColorMask(true, true, true, false);

View file

@ -47,6 +47,9 @@ public:
//// Renders a wireframe cube with the simple program.
void renderWireCube(float size);
//// Renders a solid cone with the simple program.
void renderSolidCone(float base, float height, int slices, int stacks);
/// Adds a point light to render for the current frame.
void addPointLight(const glm::vec3& position, float radius, const glm::vec3& ambient = glm::vec3(0.0f, 0.0f, 0.0f),

View file

@ -110,15 +110,18 @@ void GeometryCache::renderHemisphere(int slices, int stacks) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
const int NUM_VERTICES_PER_TRIANGLE = 3;
const int NUM_TRIANGLES_PER_QUAD = 2;
const int NUM_VERTICES_PER_TRIANGULATED_QUAD = NUM_VERTICES_PER_TRIANGLE * NUM_TRIANGLES_PER_QUAD;
const int NUM_COORDS_PER_VERTEX = 3;
const int NUM_BYTES_PER_VERTEX = NUM_COORDS_PER_VERTEX * sizeof(GLfloat);
const int NUM_BYTES_PER_INDEX = sizeof(GLushort);
void GeometryCache::renderSphere(float radius, int slices, int stacks) {
VerticesIndices& vbo = _sphereVBOs[IntPair(slices, stacks)];
int vertices = slices * (stacks - 1) + 2;
const int NUM_VERTICES_PER_TRIANGLE = 3;
const int NUM_TRIANGLES_PER_QUAD = 2;
int indices = slices * NUM_TRIANGLES_PER_QUAD * NUM_VERTICES_PER_TRIANGLE * (stacks - 1) + slices * NUM_TRIANGLES_PER_QUAD * NUM_VERTICES_PER_TRIANGLE;
if (vbo.first == 0) {
const int NUM_COORDS_PER_VERTEX = 3;
int vertices = slices * (stacks - 1) + 2;
int indices = slices * stacks * NUM_VERTICES_PER_TRIANGULATED_QUAD;
if (vbo.first == 0) {
GLfloat* vertexData = new GLfloat[vertices * NUM_COORDS_PER_VERTEX];
GLfloat* vertex = vertexData;
@ -148,8 +151,7 @@ void GeometryCache::renderSphere(float radius, int slices, int stacks) {
glGenBuffers(1, &vbo.first);
glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
const int BYTES_PER_VERTEX = NUM_COORDS_PER_VERTEX * sizeof(GLfloat);
glBufferData(GL_ARRAY_BUFFER, vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices * NUM_BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
delete[] vertexData;
GLushort* indexData = new GLushort[indices];
@ -192,8 +194,7 @@ void GeometryCache::renderSphere(float radius, int slices, int stacks) {
glGenBuffers(1, &vbo.second);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
const int BYTES_PER_INDEX = sizeof(GLushort);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * NUM_BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
delete[] indexData;
} else {
@ -239,8 +240,7 @@ void GeometryCache::renderSquare(int xDivisions, int yDivisions) {
glGenBuffers(1, &vbo.first);
glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
const int BYTES_PER_VERTEX = 3 * sizeof(GLfloat);
glBufferData(GL_ARRAY_BUFFER, vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices * NUM_BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
delete[] vertexData;
GLushort* indexData = new GLushort[indices];
@ -263,8 +263,7 @@ void GeometryCache::renderSquare(int xDivisions, int yDivisions) {
glGenBuffers(1, &vbo.second);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
const int BYTES_PER_INDEX = sizeof(GLushort);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * NUM_BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
delete[] indexData;
} else {
@ -313,8 +312,7 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) {
glGenBuffers(1, &vbo.first);
glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
const int BYTES_PER_VERTEX = 3 * sizeof(GLfloat);
glBufferData(GL_ARRAY_BUFFER, 2 * vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, 2 * vertices * NUM_BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
delete[] vertexData;
GLushort* indexData = new GLushort[indices];
@ -337,8 +335,7 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) {
glGenBuffers(1, &vbo.second);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
const int BYTES_PER_INDEX = sizeof(GLushort);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * NUM_BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
delete[] indexData;
} else {
@ -360,6 +357,106 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void GeometryCache::renderCone(float base, float height, int slices, int stacks) {
VerticesIndices& vbo = _halfCylinderVBOs[IntPair(slices, stacks)];
int vertices = (stacks + 2) * slices;
int baseTriangles = slices - 2;
int indices = NUM_VERTICES_PER_TRIANGULATED_QUAD * slices * stacks + NUM_VERTICES_PER_TRIANGLE * baseTriangles;
if (vbo.first == 0) {
GLfloat* vertexData = new GLfloat[vertices * NUM_COORDS_PER_VERTEX * 2];
GLfloat* vertex = vertexData;
// cap
for (int i = 0; i < slices; i++) {
float theta = TWO_PI * i / slices;
//normals
*(vertex++) = 0.0f;
*(vertex++) = 0.0f;
*(vertex++) = -1.0f;
// vertices
*(vertex++) = cosf(theta);
*(vertex++) = sinf(theta);
*(vertex++) = 0.0f;
}
// body
for (int i = 0; i <= stacks; i++) {
float z = (float)i / stacks;
float radius = 1.0f - z;
for (int j = 0; j < slices; j++) {
float theta = TWO_PI * j / slices;
//normals
*(vertex++) = cosf(theta) / SQUARE_ROOT_OF_2;
*(vertex++) = sinf(theta) / SQUARE_ROOT_OF_2;
*(vertex++) = 1.0f / SQUARE_ROOT_OF_2;
// vertices
*(vertex++) = radius * cosf(theta);
*(vertex++) = radius * sinf(theta);
*(vertex++) = z;
}
}
glGenBuffers(1, &vbo.first);
glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
glBufferData(GL_ARRAY_BUFFER, 2 * vertices * NUM_BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
delete[] vertexData;
GLushort* indexData = new GLushort[indices];
GLushort* index = indexData;
for (int i = 0; i < baseTriangles; i++) {
*(index++) = 0;
*(index++) = i + 1;
*(index++) = i + 2;
}
for (int i = 1; i <= stacks; i++) {
GLushort bottom = i * slices;
GLushort top = bottom + slices;
for (int j = 0; j < slices; j++) {
int next = (j + 1) % slices;
*(index++) = bottom + j;
*(index++) = top + next;
*(index++) = top + j;
*(index++) = bottom + j;
*(index++) = bottom + next;
*(index++) = top + next;
}
}
glGenBuffers(1, &vbo.second);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * NUM_BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
delete[] indexData;
} else {
glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
int stride = NUM_VERTICES_PER_TRIANGULATED_QUAD * sizeof(float);
glNormalPointer(GL_FLOAT, stride, 0);
glVertexPointer(NUM_COORDS_PER_VERTEX, GL_FLOAT, stride, (const void *)(NUM_COORDS_PER_VERTEX * sizeof(float)));
glPushMatrix();
glScalef(base, base, height);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertices - 1, indices, GL_UNSIGNED_SHORT, 0);
glPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void GeometryCache::renderGrid(int xDivisions, int yDivisions) {
QOpenGLBuffer& buffer = _gridBuffers[IntPair(xDivisions, yDivisions)];
int vertices = (xDivisions + 1 + yDivisions + 1) * 2;

View file

@ -42,6 +42,7 @@ public:
void renderSphere(float radius, int slices, int stacks);
void renderSquare(int xDivisions, int yDivisions);
void renderHalfCylinder(int slices, int stacks);
void renderCone(float base, float height, int slices, int stacks);
void renderGrid(int xDivisions, int yDivisions);
/// Loads geometry from the specified URL.
@ -71,6 +72,7 @@ private:
QHash<IntPair, VerticesIndices> _sphereVBOs;
QHash<IntPair, VerticesIndices> _squareVBOs;
QHash<IntPair, VerticesIndices> _halfCylinderVBOs;
QHash<IntPair, VerticesIndices> _coneVBOs;
QHash<IntPair, QOpenGLBuffer> _gridBuffers;
QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry;