EDIT: Demo finally online: http://bharling.github.io/deferred/index.html use the dropdown to switch to torus models to see the problem live. NOTE: requires Webgl MRT extensions.
I've been in the process of developing my own deferred rendering engine in WebGL for some time now and have got to the stage where I have a working prototype using GBuffers and MRT extensions that renders some teapots quite satisfactorily. This is developed from scratch mainly for me to learn WebGL properly without using any frameworks, and to understand deferred rendering. For anyone interested - the source is on github here: https://github.com/bharling/webgl-defer
I've got to the stage where I'm tired of just seeing teapots and have tried to implement a loader for THREE.js JSON format models. I've ported ( copied ) the main parts of the loader and I can get meshes to appear with the correct vertices and index buffers which is great, but normals are consistently screwy. I'm choosing only to support indexed geometry with vertex UVs and vertex normals and a single material ( eventually this is supposed to be PBR based ), so I ignore anything else in the JSON and write only what I support straight to Float32Arrays (etc). Below is my import code, plus a screenshot of the weird normals I'm seeing.
parseThreeJSModel: (data) =>
isBitSet = (value, position) ->
return value & ( 1 << position )
vertices = data.vertices
uvs = data.uvs
indices = []
normals = data.normals
vertexNormals = []
vertexUvs = []
vertexPositions = []
@vertexPositionBuffer = new DFIR.Buffer( new Float32Array( data.vertices ), 3, gl.STATIC_DRAW )
@vertexTextureCoordBuffer = new DFIR.Buffer( new Float32Array( data.uvs[0] ), 2, gl.STATIC_DRAW )
numUvLayers = data.uvs.length
faces = data.faces
zLength = faces.length
offset = 0
while offset < zLength
type = faces[offset++]
isQuad = isBitSet( type, 0 )
hasMaterial = isBitSet( type, 1 )
hasFaceVertexUv = isBitSet( type, 3 )
hasFaceNormal = isBitSet( type, 4 )
hasFaceVertexNormal = isBitSet( type, 5 )
hasFaceColor = isBitSet( type, 6 )
hasFaceVertexColor = isBitSet( type, 7 )
if isQuad
indices.push faces[ offset ]
indices.push faces[ offset + 1 ]
indices.push faces[ offset + 3 ]
indices.push faces[ offset + 1 ]
indices.push faces[ offset + 2 ]
indices.push faces[ offset + 3 ]
offset += 4
if hasMaterial
offset++
if hasFaceVertexUv
for i in [0 ... numUvLayers] by 1
uvLayer = data.uvs[i]
for j in [0 ... 4] by 1
uvIndex = faces[offset++]
u = uvLayer[ uvIndex * 2 ]
v = uvLayer[ uvIndex * 2 + 1 ]
if j isnt 2
vertexUvs.push u
vertexUvs.push v
if j isnt 0
vertexUvs.push u
vertexUvs.push v
if hasFaceNormal
offset++
if hasFaceVertexNormal
for i in [0 ... 4] by 1
normalIndex = faces[ offset++ ] * 3
normal = [ normalIndex++, normalIndex++, normalIndex ]
if i isnt 2
vertexNormals.push normals[normal[0]]
vertexNormals.push normals[normal[1]]
vertexNormals.push normals[normal[2]]
if i isnt 0
vertexNormals.push normals[normal[0]]
vertexNormals.push normals[normal[1]]
vertexNormals.push normals[normal[2]]
if hasFaceColor
offset++
if hasFaceVertexColor
offset += 4
else
indices.push faces[offset++]
indices.push faces[offset++]
indices.push faces[offset++]
if hasMaterial
offset++
if hasFaceVertexUv
for i in [0 ... numUvLayers] by 1
uvLayer = data.uvs[i]
for j in [0 ... 3] by 1
uvIndex = faces[offset++]
u = uvLayer[ uvIndex * 2 ]
v = uvLayer[ uvIndex * 2 + 1 ]
if j isnt 2
vertexUvs.push u
vertexUvs.push v
if j isnt 0
vertexUvs.push u
vertexUvs.push v
if hasFaceNormal
offset++
if hasFaceVertexNormal
for i in [0 ... 3] by 1
normalIndex = faces[ offset++ ] * 3
vertexNormals.push normals[normalIndex++]
vertexNormals.push normals[normalIndex++]
vertexNormals.push normals[normalIndex]
if hasFaceColor
offset++
if hasFaceVertexColor
offset +=3
@vertexNormalBuffer = new DFIR.Buffer( new Float32Array( vertexNormals ), 3, gl.STATIC_DRAW )
@vertexIndexBuffer = new DFIR.Buffer( new Uint16Array( indices ), 1, gl.STATIC_DRAW, gl.ELEMENT_ARRAY_BUFFER )
@loaded=true
Screenshot above should be the expanded world-space normals gbuffer.
One big difference in my engine is that I don't store face information in classes ( like THREE.Face3 ) preferring just to write the data straight into buffer attributes, more like THREE.BufferGeometry.
Up until now I have been using just the utah teapot model from the 'Learning WebGL' course, specifically this link http://learningwebgl.com/blog/?p=1658 . This model works exactly right in my engine, and is supposedly an early version of the THREE JSON format. I load that model as in the tutorial by writing the json arrays for vertices, texcoords etc straight to webgl buffers, and this works perfectly in my engine, but even a simple cube exported from the latest blender exporter doesn't seem to work so well.
Any suggestions greatly appreciated, thanks!
EDIT: Screenshot of normals pass using the teapot model from webgl tutorials. Note: I'm not suggesting the THREE exporter is broken, rather my code that parses it. I've been over the GBuffer implementation many times in this engine over the last year or so, and am pretty sure this is correct now, Im just having a bit of problems understanding the THREE json model format.
It is not directly the solution to your problem, but might be helpful anyway.
If you have primitive shapes like the boxes in the example above, you can also use
THREE.FlatShading
and skip setting normals all together. Might be easier in some cases: