Showing posts with label Volumetric Lighting. Show all posts
Showing posts with label Volumetric Lighting. Show all posts

Wednesday, April 25, 2012

[VolumetricLighting] Final Demo

Slides:


Demos (Fixed a glitch. Now you can view them in both firefox and chrome):
  1. gears
  2. boxes
Report:
Video: http://dl.dropbox.com/u/4237388/VolumetricLightingFinalVideoADL.mp4
Download code here.

Wednesday, April 18, 2012

[VolumetricLighting] additive blending

I finally figured out how to *additively blend* the following two passes to get the final render:
The additive blending equation looks like this: colorFinal = (colorTop)*(alphaTop) + colorBottom
So when the bottom image is black (0,0,0), the final color will be the top image color. And when the top image is transparent (alpha=0), then the final color will be the bottom pixel.
This is different from the blending method I took to blend the untextured objects and the light source background image, where I set the final color to be bottom pixel if the top pixel's alpha value is 0, and otherwise, set the final color to be the top pixel color. One very important things is that when we clear the screen, we should set the alpha channel of the background color to be 0.0 (because we don't want it to contribute to our final rendering).

Wednesday, April 11, 2012

[VolumetricLighting] render into texture

Since our occlusion pre-pass method requires multiple passes before the final composition, we need to render each pass into a texture such that we can feed it into the next pixel shader program.
Thanks to this wonderful tutorial, I figured out how to render a 3D scene into a texture.
To render into a texture instead of the default view, we first need to create a new framebuffer object:
1:  var blackFramebuffer = gl.createFramebuffer();  
2:  gl.bindFramebuffer(gl.FRAMEBUFFER, blackFramebuffer);  
3:  blackFramebuffer.width = 512;  
4:  blackFramebuffer.height = 512;  
5:  blackTexture = gl.createTexture();  
6:  gl.bindTexture(gl.TEXTURE_2D, blackTexture);  
7:  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);  
8:  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);  
9:  gl.generateMipmap(gl.TEXTURE_2D);  
10:  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, blackFramebuffer.width, blackFramebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);  
11:  var renderbuffer0 = gl.createRenderbuffer();  
12:  gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer0);  
13:  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, blackFramebuffer.width, blackFramebuffer.height);  
14:  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, blackTexture, 0);  
15:  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer0);  
16:  gl.bindTexture(gl.TEXTURE_2D, null);  
17:  gl.bindRenderbuffer(gl.RENDERBUFFER, null);  
18:  gl.bindFramebuffer(gl.FRAMEBUFFER, null);  
Then in the beginning of my draw() function, we add the following:
 gl.bindFramebuffer(gl.FRAMEBUFFER, blackFramebuffer);  
And after calling gl.drawElements() in my draw() function, we add the following:
 gl.bindTexture(gl.TEXTURE_2D, blackTexture);  
 gl.generateMipmap(gl.TEXTURE_2D);  
 gl.bindTexture(gl.TEXTURE_2D, null);  

Monday, April 9, 2012

[VolumetricLighting] post process shader

I have not figured out how to blend the rendered object with a bitmap light source, but I need this blended image to activate my volumetric lighting pixel shader. However, I realized that the calculation inside the post-process volumetric lighting shader is completely on screen space (which makes it super fast!). So I came up a way to test my volumetric lighting shader without the blended image.
It's pretty simple: I just manually drew solid black shapes on top of a light source image. I also made the light source image myself: I drew a circle with a gradient from yellow to black in Photoshop on top of a black layer; merged the two layers and applied a Gaussian filter to smooth it out. Then I load I just loaded the light source image with hand-drawn black solid shapes as a regular texture image and feed it into the volumetric lighting shader. It does generate volumetric lighting effects on this image (yay!). I tuned the constant parameters (EXPOSURE, DECAY, WEIGHT, NUM_OF_SAMPLES) to achieve better effects. See the attached pic for some results I got. I also got some crazy results when the light source itself contains many black streaks. With EXPOSURE = 0.3, DECAY = 0.9, DENSITY = 5.3, WEIGHT = 0.7, and NUM_OF_SAMPLES = 250:

Thursday, April 5, 2012

[VolumetricLighting] multiple programs

Each shaderProgram can only have one vertex shader and one fragment shader. What if we want to use different shader to shade different part of our view? Or in our volumetric lighting case, we want to enable different fragment shaders for different passes. I was inspired by the last assignment of CIS565 where we wrote a post-process image processing library. We can switch to different shaderPrograms according to the pressedkey. So for my volumetric lighting project, I initiated a bunch of different shaderProgram (all links to the same vertex shader but different fragment shaders), and switched to the proper one before drawing each pass.
  function createProgram(fragmentShaderID, vertexShaderID) { 
     var fragmentShader = getShader(gl, fragmentShaderID); 
     var vertexShader = getShader(gl, vertexShaderID); 
     var program = gl.createProgram(); 
     gl.attachShader(program, vertexShader); 
     gl.attachShader(program, fragmentShader); 
     gl.linkProgram(program); 
     if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 
       alert("Could not initialise shaders"); 
     } 
     program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); 
     gl.enableVertexAttribArray(program.vertexPositionAttribute); 
     program.vertexNormalAttribute = gl.getAttribLocation(program, "aVertexNormal"); 
     gl.enableVertexAttribArray(program.vertexNormalAttribute); 
     program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); 
     gl.enableVertexAttribArray(program.textureCoordAttribute); 
             program.samplerUniform = gl.getUniformLocation(program, "uSampler"); 
             program.pMatrixUniform = gl.getUniformLocation(program, "uPMatrix"); 
     program.mvMatrixUniform = gl.getUniformLocation(program, "uMVMatrix"); 
     return program; 
   } 
   var currentProgram; 
   var regProgram; 
   var postProgram; 
       var blendProgram; 
       var occProgram; 
 function initShaders() { 
 regProgram = createProgram("reg-fs", "vs"); 
 regProgram.drawblackUniform = gl.getUniformLocation(regProgram, "drawblack"); 
 postProgram = createProgram("post-process-fs", "vs"); 
 occProgram = createProgram("occlusion-fs", "vs"); 
 occProgram.maskUniform = gl.getUniformLocation(occProgram, "mask"); 
 blendProgram = createProgram("blend-fs", "vs"); 
 blendProgram.maskUniform = gl.getUniformLocation(blendProgram, "mask"); 
 } 

Monday, March 26, 2012

[VolumetricLighting] convert obj to json

I modeled a gear in 3ds MAX (make sure it's centered at (0,0,0) ) and export it to obj format (triangle mesh).
I would like to load the gear model in my application. However, I only learned from the tutorial that we can parse JSON format (which is already written in JavaScript) in JavaScript:

  function loadModel() {  
   var request = new XMLHttpRequest();  
   request.open("GET", "model.json");  
   request.onreadystatechange = function() {  
    if (request.readyState == 4) {  
     handleLoadedModel(JSON.parse(request.responseText));  
    }  
   }  
   request.send();  
  }  
The above lines create a new XMLHttpRequest (comment: Chrome isn't happy with local request and keeps giving an "Cross origin requests are only supported for HTTP" error message.) and use it to load the file Teapot.json, and trigger an action that will convert the JSON text into the data we can use:
1:    var modelVertexPositionBuffer;  
2:    var modelVertexNormalBuffer;  
3:    var modelVertexTextureCoordBuffer;  
4:    var modelVertexIndexBuffer;  
5:    function handleLoadedModel(modelData) {  
6:      modelVertexNormalBuffer = gl.createBuffer();  
7:      gl.bindBuffer(gl.ARRAY_BUFFER, modelVertexNormalBuffer);  
8:      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(modelData.vertexNormals), gl.STATIC_DRAW);  
9:      modelVertexNormalBuffer.itemSize = 3;  
10:      modelVertexNormalBuffer.numItems = modelData.vertexNormals.length / 3;  
11:      modelVertexTextureCoordBuffer = gl.createBuffer();  
12:      gl.bindBuffer(gl.ARRAY_BUFFER, modelVertexTextureCoordBuffer);  
13:      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(modelData.vertexTextureCoords), gl.STATIC_DRAW);  
14:      modelVertexTextureCoordBuffer.itemSize = 2;  
15:      modelVertexTextureCoordBuffer.numItems = modelData.vertexTextureCoords.length / 2;  
16:      modelVertexPositionBuffer = gl.createBuffer();  
17:      gl.bindBuffer(gl.ARRAY_BUFFER, modelVertexPositionBuffer);  
18:      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(modelData.vertexPositions), gl.STATIC_DRAW);  
19:      modelVertexPositionBuffer.itemSize = 3;  
20:      modelVertexPositionBuffer.numItems = modelData.vertexPositions.length / 3;  
21:      modelVertexIndexBuffer = gl.createBuffer();  
22:      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, modelVertexIndexBuffer);  
23:      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(modelData.indices), gl.STREAM_DRAW);  
24:      modelVertexIndexBuffer.itemSize = 1;  
25:      modelVertexIndexBuffer.numItems = modelData.indices.length;  
26:    }  
So I'm searching for a method to convert obj to json. Attached is a python script that will do the job for us!
To use the script in windows, open a command line and cd to the dir where your obj file is located. Put this script in the same directory and type the following in the command line:

 python obj2json.py gear.obj > gear.json  

Monday, March 12, 2012

[VolumetricLighting] Final Project Proposal

Here is the PDF version of my final project proposal:

Here is the demo video for Mitchell's article:

After discussion with Patrick, I've decided to implement my project in JavaScript and WebGL and will analyze its performance on dynamic scenes with Fraps.