2 * Copyright 1993-2013 NVIDIA Corporation. All rights reserved.
4 * Please refer to the NVIDIA end user license agreement (EULA) associated
5 * with this source code for terms and conditions that govern your use of
6 * this software. Any use, reproduction, disclosure, or distribution of
7 * this software and related documentation outside the terms of the EULA
8 * is strictly prohibited.
13 This class renders particles using OpenGL and GLSL shaders
18 #include "SmokeRenderer.h"
19 #include "SmokeShaders.h"
21 #if defined(__APPLE__) || defined(MACOSX)
22 #include <GLUT/glut.h>
24 #include <GL/freeglut.h>
28 #define COLOR_ATTENUATION 1
30 SmokeRenderer::SmokeRenderer(int maxParticles) :
31 mMaxParticles(maxParticles),
37 mParticleRadius(0.005f),
38 mDisplayMode(VOLUMETRIC),
44 m_numDisplayedSlices(32),
46 m_shadowAlpha(0.005f),
50 m_displayLightBuffer(false),
51 m_lightPos(5.0f, 5.0f, -5.0f),
52 m_lightTarget(0.0f, 0.0f, 0.0f),
53 m_lightColor(1.0f, 1.0f, 0.5f),
54 m_colorAttenuation(0.1f, 0.2f, 0.3f),
55 m_lightBufferSize(256),
57 m_lightDepthTexture(0),
63 // load shader programs
64 m_simpleProg = new GLSLProgram(particleVS, simplePS);
65 m_particleProg = new GLSLProgram(mblurVS, mblurGS, particlePS);
66 m_particleShadowProg = new GLSLProgram(mblurVS, mblurGS, particleShadowPS);
68 m_blurProg = new GLSLProgram(passThruVS, blurPS);
69 m_displayTexProg = new GLSLProgram(passThruVS, texture2DPS);
71 // create buffer for light shadows
78 SmokeRenderer::~SmokeRenderer()
80 delete m_particleProg;
81 delete m_particleShadowProg;
83 delete m_displayTexProg;
87 glDeleteTextures(2, m_lightTexture);
88 glDeleteTextures(1, &m_lightDepthTexture);
91 glDeleteTextures(1, &m_imageTex);
92 glDeleteTextures(1, &m_depthTex);
95 // draw points from vertex buffer objects
96 void SmokeRenderer::drawPoints(int start, int count, bool sort)
98 glBindBufferARB(GL_ARRAY_BUFFER_ARB, mPosVbo);
99 glVertexPointer(4, GL_FLOAT, 0, 0);
100 glEnableClientState(GL_VERTEX_ARRAY);
104 glBindBufferARB(GL_ARRAY_BUFFER_ARB, mColorVbo);
105 glColorPointer(4, GL_FLOAT, 0, 0);
106 glEnableClientState(GL_COLOR_ARRAY);
111 glBindBufferARB(GL_ARRAY_BUFFER_ARB, mVelVbo);
112 glClientActiveTexture(GL_TEXTURE0);
113 glTexCoordPointer(4, GL_FLOAT, 0, 0);
114 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
119 glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mIndexBuffer);
120 glDrawElements(GL_POINTS, count, GL_UNSIGNED_INT, (void *)(start*sizeof(unsigned int)));
121 glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
125 glDrawArrays(GL_POINTS, start, count);
128 glDisableClientState(GL_VERTEX_ARRAY);
129 glDisableClientState(GL_COLOR_ARRAY);
131 glClientActiveTexture(GL_TEXTURE0);
132 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
135 // draw points using given shader program
136 void SmokeRenderer::drawPointSprites(GLSLProgram *prog, int start, int count, bool shadowed)
138 glEnable(GL_DEPTH_TEST);
139 glDepthMask(GL_FALSE); // don't write depth
143 prog->setUniform1f("pointRadius", mParticleRadius);
147 prog->bindTexture("shadowTex", m_lightTexture[m_srcLightTexture], GL_TEXTURE_2D, 0);
151 drawPoints(start, count, true);
155 glDepthMask(GL_TRUE);
159 // calculate vectors for half-angle slice rendering
160 void SmokeRenderer::calcVectors()
162 // get model view matrix
163 glGetFloatv(GL_MODELVIEW_MATRIX, (float *) m_modelView.get_value());
165 // calculate eye space light vector
166 m_lightVector = normalize(m_lightPos);
167 m_lightPosEye = m_modelView * vec4f(m_lightPos, 1.0);
169 // calculate half-angle vector between view and light
170 m_viewVector = -vec3f(m_modelView.get_row(2));
172 if (dot(m_viewVector, m_lightVector) > 0)
174 m_halfVector = normalize(m_viewVector + m_lightVector);
175 m_invertedView = false;
179 m_halfVector = normalize(-m_viewVector + m_lightVector);
180 m_invertedView = true;
183 // calculate light view matrix
184 glMatrixMode(GL_MODELVIEW);
187 gluLookAt(m_lightPos[0], m_lightPos[1], m_lightPos[2],
188 m_lightTarget[0], m_lightTarget[1], m_lightTarget[2],
191 // calculate light projection matrix
192 glMatrixMode(GL_PROJECTION);
195 gluPerspective(45.0, 1.0, 1.0, 200.0);
197 glGetFloatv(GL_MODELVIEW_MATRIX, (float *) m_lightView.get_value());
198 glGetFloatv(GL_PROJECTION_MATRIX, (float *) m_lightProj.get_value());
200 glMatrixMode(GL_PROJECTION);
202 glMatrixMode(GL_MODELVIEW);
205 // construct shadow matrix
207 scale.set_scale(vec3f(0.5, 0.5, 0.5));
209 translate.set_translate(vec3f(0.5, 0.5, 0.5));
211 m_shadowMatrix = translate * scale * m_lightProj * m_lightView * inverse(m_modelView);
213 // calc object space eye position
214 m_eyePos = inverse(m_modelView) * vec4f(0.0, 0.0, 0.0, 1.0);
216 // calc half vector in eye space
217 m_halfVectorEye = m_modelView * vec4f(m_halfVector, 0.0);
220 // draw slice of particles from camera view
221 void SmokeRenderer::drawSlice(int i)
224 glViewport(0, 0, m_imageW, m_imageH);
226 glColor4f(1.0, 1.0, 1.0, m_spriteAlpha);
231 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
236 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
239 drawPointSprites(m_particleShadowProg, i*m_batchSize, m_batchSize, true);
241 m_imageFbo->Disable();
244 // draw slice of particles from light's point of view
245 void SmokeRenderer::drawSliceLightView(int i)
247 glMatrixMode(GL_MODELVIEW);
249 glLoadMatrixf((GLfloat *) m_lightView.get_value());
251 glMatrixMode(GL_PROJECTION);
253 glLoadMatrixf((GLfloat *) m_lightProj.get_value());
256 glViewport(0, 0, m_lightBufferSize, m_lightBufferSize);
258 glColor4f(m_colorAttenuation[0] * m_shadowAlpha, m_colorAttenuation[1] * m_shadowAlpha, m_colorAttenuation[2] * m_shadowAlpha, 1.0);
259 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
261 drawPointSprites(m_particleProg, i*m_batchSize, m_batchSize, false);
263 m_lightFbo->Disable();
265 glMatrixMode(GL_PROJECTION);
267 glMatrixMode(GL_MODELVIEW);
271 // draw particles as slices with shadowing
272 void SmokeRenderer::drawSlices()
274 m_batchSize = mNumParticles / m_numSlices;
276 // clear light buffer
277 m_srcLightTexture = 0;
279 m_lightFbo->AttachTexture(GL_TEXTURE_2D, m_lightTexture[m_srcLightTexture], GL_COLOR_ATTACHMENT0_EXT);
280 glClearColor(1.0f - m_lightColor[0], 1.0f - m_lightColor[1], 1.0f - m_lightColor[2], 0.0f);
281 glClear(GL_COLOR_BUFFER_BIT);
282 m_lightFbo->Disable();
284 // clear volume image
286 glClearColor(0.0, 0.0, 0.0, 0.0);
287 glClear(GL_COLOR_BUFFER_BIT);
288 m_imageFbo->Disable();
290 glActiveTexture(GL_TEXTURE0);
291 glMatrixMode(GL_TEXTURE);
292 glLoadMatrixf((GLfloat *) m_shadowMatrix.get_value());
295 if (m_numDisplayedSlices > m_numSlices) m_numDisplayedSlices = m_numSlices;
297 for (int i=0; i<m_numDisplayedSlices; i++)
299 // draw slice from camera view, sampling light buffer
301 // draw slice from light view to light buffer, accumulating shadows
302 drawSliceLightView(i);
310 glActiveTexture(GL_TEXTURE0);
311 glMatrixMode(GL_TEXTURE);
315 // blur light buffer to simulate scattering effects
316 void SmokeRenderer::blurLightBuffer()
319 m_lightFbo->AttachTexture(GL_TEXTURE_2D, m_lightTexture[1 - m_srcLightTexture], GL_COLOR_ATTACHMENT0_EXT);
320 glViewport(0, 0, m_lightBufferSize, m_lightBufferSize);
322 m_blurProg->enable();
323 m_blurProg->bindTexture("tex", m_lightTexture[m_srcLightTexture], GL_TEXTURE_2D, 0);
324 m_blurProg->setUniform2f("texelSize", 1.0f / (float) m_lightBufferSize, 1.0f / (float) m_lightBufferSize);
325 m_blurProg->setUniform1f("blurRadius", m_blurRadius);
326 glDisable(GL_DEPTH_TEST);
328 m_blurProg->disable();
330 m_srcLightTexture = 1 - m_srcLightTexture;
332 m_lightFbo->Disable();
335 // display texture to screen
336 void SmokeRenderer::displayTexture(GLuint tex)
338 m_displayTexProg->enable();
339 m_displayTexProg->bindTexture("tex", tex, GL_TEXTURE_2D, 0);
341 m_displayTexProg->disable();
344 // composite final volume image on top of scene
345 void SmokeRenderer::compositeResult()
347 glViewport(0, 0, mWindowW, mWindowH);
348 glDisable(GL_DEPTH_TEST);
349 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
351 displayTexture(m_imageTex);
355 void SmokeRenderer::render()
357 switch (mDisplayMode)
360 glColor3f(1.0, 1.0, 1.0);
361 m_simpleProg->enable();
362 drawPoints(0, mNumParticles, false);
363 m_simpleProg->disable();
367 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
368 glColor4f(1.0, 1.0, 1.0, m_spriteAlpha);
369 drawPointSprites(m_particleProg, 0, mNumParticles, false);
381 if (m_displayLightBuffer)
383 // display light buffer to screen
384 glViewport(0, 0, m_lightBufferSize, m_lightBufferSize);
385 glDisable(GL_DEPTH_TEST);
386 displayTexture(m_lightTexture[m_srcLightTexture]);
387 glViewport(0, 0, mWindowW, mWindowH);
393 // render scene depth to texture
394 // (this is to ensure that particle are correctly occluded in the low-resolution render buffer)
395 void SmokeRenderer::beginSceneRender(Target target)
397 if (target == LIGHT_BUFFER)
400 glViewport(0, 0, m_lightBufferSize, m_lightBufferSize);
402 glMatrixMode(GL_MODELVIEW);
404 glLoadMatrixf((GLfloat *) m_lightView.get_value());
406 glMatrixMode(GL_PROJECTION);
408 glLoadMatrixf((GLfloat *) m_lightProj.get_value());
413 glViewport(0, 0, m_imageW, m_imageH);
416 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
417 glDepthMask(GL_TRUE);
418 glClear(GL_DEPTH_BUFFER_BIT);
421 void SmokeRenderer::endSceneRender(Target target)
423 if (target == LIGHT_BUFFER)
425 m_lightFbo->Disable();
426 glMatrixMode(GL_PROJECTION);
428 glMatrixMode(GL_MODELVIEW);
433 m_imageFbo->Disable();
436 glViewport(0, 0, mWindowW, mWindowH);
437 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
440 // create an OpenGL texture
442 SmokeRenderer::createTexture(GLenum target, int w, int h, GLint internalformat, GLenum format)
445 glGenTextures(1, &texid);
446 glBindTexture(target, texid);
448 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
449 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
450 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
451 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
453 glTexImage2D(target, 0, internalformat, w, h, 0, format, GL_FLOAT, 0);
457 // create buffers for off-screen rendering
458 void SmokeRenderer::createBuffers(int w, int h)
462 glDeleteTextures(1, &m_imageTex);
463 glDeleteTextures(1, &m_depthTex);
470 m_imageW = w / m_downSample;
471 m_imageH = h / m_downSample;
473 // create fbo for image buffer
474 GLint format = GL_RGBA16F_ARB;
475 //GLint format = GL_LUMINANCE16F_ARB;
476 //GLint format = GL_RGBA8;
477 m_imageTex = createTexture(GL_TEXTURE_2D, m_imageW, m_imageH, format, GL_RGBA);
478 m_depthTex = createTexture(GL_TEXTURE_2D, m_imageW, m_imageH, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT);
479 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
480 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
482 m_imageFbo = new FramebufferObject();
483 m_imageFbo->AttachTexture(GL_TEXTURE_2D, m_imageTex, GL_COLOR_ATTACHMENT0_EXT);
484 m_imageFbo->AttachTexture(GL_TEXTURE_2D, m_depthTex, GL_DEPTH_ATTACHMENT_EXT);
485 m_imageFbo->IsValid();
489 SmokeRenderer::setLightColor(vec3f c)
493 // set light texture border color
494 GLfloat borderColor[4] = { 1.0f - m_lightColor[0], 1.0f - m_lightColor[1], 1.0f - m_lightColor[2], 0.0f };
496 glBindTexture(GL_TEXTURE_2D, m_lightTexture[0]);
497 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
499 glBindTexture(GL_TEXTURE_2D, m_lightTexture[1]);
500 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
502 glBindTexture(GL_TEXTURE_2D, 0);
506 // create FBOs for light buffer
508 SmokeRenderer::createLightBuffer()
510 GLint format = GL_RGBA16F_ARB;
511 //GLint format = GL_RGBA8;
512 //GLint format = GL_LUMINANCE16F_ARB;
514 m_lightTexture[0] = createTexture(GL_TEXTURE_2D, m_lightBufferSize, m_lightBufferSize, format, GL_RGBA);
515 // make shadows clamp to light color at edges
516 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
517 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
519 m_lightTexture[1] = createTexture(GL_TEXTURE_2D, m_lightBufferSize, m_lightBufferSize, format, GL_RGBA);
520 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
521 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
523 m_lightDepthTexture = createTexture(GL_TEXTURE_2D, m_lightBufferSize, m_lightBufferSize, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT);
525 m_lightFbo = new FramebufferObject();
526 m_lightFbo->AttachTexture(GL_TEXTURE_2D, m_lightTexture[m_srcLightTexture], GL_COLOR_ATTACHMENT0_EXT);
527 m_lightFbo->AttachTexture(GL_TEXTURE_2D, m_lightDepthTexture, GL_DEPTH_ATTACHMENT_EXT);
528 m_lightFbo->IsValid();
531 void SmokeRenderer::setWindowSize(int w, int h)
533 mAspect = (float) mWindowW / (float) mWindowH;
534 mInvFocalLen = tan(mFov*0.5f*NV_PI/180.0f);
539 void SmokeRenderer::drawQuad()
542 glTexCoord2f(0.0f, 0.0f);
543 glVertex2f(-1.0f, -1.0f);
544 glTexCoord2f(1.0f, 0.0f);
545 glVertex2f(1.0f, -1.0f);
546 glTexCoord2f(1.0f, 1.0f);
547 glVertex2f(1.0f, 1.0f);
548 glTexCoord2f(0.0f, 1.0f);
549 glVertex2f(-1.0f, 1.0f);
553 void SmokeRenderer::drawVector(vec3f v)
556 glVertex3f(0.0f, 0.0f, 0.0f);
557 glVertex3fv((float *) &v[0]);
561 // render vectors to screen for debugging
562 void SmokeRenderer::debugVectors()
564 glColor3f(1.0f, 1.0f, 0.0f);
565 drawVector(m_lightVector);
567 glColor3f(0.0f, 1.0f, 0.0f);
568 drawVector(m_viewVector);
570 glColor3f(0.0f, 0.0f, 1.0f);
571 drawVector(-m_viewVector);
573 glColor3f(1.0f, 0.0f, 0.0f);
574 drawVector(m_halfVector);