diff --git a/examples/materials_normalmap.html b/examples/materials_normalmap.html new file mode 100644 index 00000000..adf79173 --- /dev/null +++ b/examples/materials_normalmap.html @@ -0,0 +1,282 @@ + + + + three.js - webgl normal map + + + + + +

+		
+		
+ three.js - webgl (normal + ao + displacement) map demo. + ninja head from AMD GPU MeshMapper + +
displacement mapping needs vertex textures (GPU with Shader Model 3.0)
+ on Windows use Chrome --use-gl=desktop or Firefox 4
+ please star this Chrome issue to get ANGLE support +
+
+ +
+
+ Sorry, your browser doesn't support WebGL + and Web Workers.
+
+ Please try in + Chrome 9+ / + Firefox 4+ / + Safari OSX 10.6+ +
+
+ + + + + + + + diff --git a/examples/obj/ninja/.htaccess b/examples/obj/ninja/.htaccess new file mode 100644 index 00000000..d9f67316 --- /dev/null +++ b/examples/obj/ninja/.htaccess @@ -0,0 +1,7 @@ + +SetOutputFilter DEFLATE + + + +SetOutputFilter DEFLATE + diff --git a/examples/obj/ninja/NinjaLo_bin.bin b/examples/obj/ninja/NinjaLo_bin.bin new file mode 100644 index 00000000..2d6ff9dc Binary files /dev/null and b/examples/obj/ninja/NinjaLo_bin.bin differ diff --git a/examples/obj/ninja/NinjaLo_bin.js b/examples/obj/ninja/NinjaLo_bin.js new file mode 100644 index 00000000..604c8e2b --- /dev/null +++ b/examples/obj/ninja/NinjaLo_bin.js @@ -0,0 +1,22 @@ +// Converted from: ../../examples/obj/ninja/ninjaHead_Low.obj +// vertices: 4485 +// faces: 4810 +// materials: 0 +// +// Generated with OBJ -> Three.js converter +// http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py + + +var model = { + 'materials': [ { + "a_dbg_color" : 0xeeeeee, + "a_dbg_index" : 0, + "a_dbg_name" : "default" + }], + + 'buffers': 'NinjaLo_bin.bin', + + 'end': (new Date).getTime() + } + +postMessage( model ); diff --git a/examples/textures/normal/ninja/ao.jpg b/examples/textures/normal/ninja/ao.jpg new file mode 100644 index 00000000..bc791d01 Binary files /dev/null and b/examples/textures/normal/ninja/ao.jpg differ diff --git a/examples/textures/normal/ninja/displacement.jpg b/examples/textures/normal/ninja/displacement.jpg new file mode 100644 index 00000000..ed8508ed Binary files /dev/null and b/examples/textures/normal/ninja/displacement.jpg differ diff --git a/examples/textures/normal/ninja/displacement.txt b/examples/textures/normal/ninja/displacement.txt new file mode 100644 index 00000000..96779902 --- /dev/null +++ b/examples/textures/normal/ninja/displacement.txt @@ -0,0 +1,2 @@ +DisplacementMap Scale: 2.436143 +DisplacementMap Bias : -0.428408 diff --git a/examples/textures/normal/ninja/normal.jpg b/examples/textures/normal/ninja/normal.jpg new file mode 100644 index 00000000..35c10158 Binary files /dev/null and b/examples/textures/normal/ninja/normal.jpg differ diff --git a/src/core/Geometry.js b/src/core/Geometry.js index aa271e73..9ec629a9 100644 --- a/src/core/Geometry.js +++ b/src/core/Geometry.js @@ -12,6 +12,8 @@ THREE.Geometry = function () { this.geometryChunks = {}; + this.hasTangents = false; + }; THREE.Geometry.prototype = { @@ -167,6 +169,121 @@ THREE.Geometry.prototype = { }, + computeTangents: function() { + + // based on http://www.terathon.com/code/tangent.html + // tangents go to vertices + + var f, fl, v, vl, face, uv, vA, vB, vC, uvA, uvB, uvC, + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r, t, n, + tan1 = [], tan2 = [], + sdir = new THREE.Vector3(), tdir = new THREE.Vector3(), + tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(), + n = new THREE.Vector3(), w; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + tan1[ v ] = new THREE.Vector3(); + tan2[ v ] = new THREE.Vector3(); + + } + + function handleTriangle( context, a, b, c ) { + + vA = context.vertices[ a ].position; + vB = context.vertices[ b ].position; + vC = context.vertices[ c ].position; + + uvA = uv[ 0 ]; + uvB = uv[ 1 ]; + uvC = uv[ 2 ]; + + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; + + s1 = uvB.u - uvA.u; + s2 = uvC.u - uvA.u; + t1 = uvB.v - uvA.v; + t2 = uvC.v - uvA.v; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + sdir.set( ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r ); + tdir.set( ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r ); + + tan1[ a ].addSelf( sdir ); + tan1[ b ].addSelf( sdir ); + tan1[ c ].addSelf( sdir ); + + tan2[ a ].addSelf( tdir ); + tan2[ b ].addSelf( tdir ); + tan2[ c ].addSelf( tdir ); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + uv = this.uvs[ f ]; + + if ( face instanceof THREE.Face3 ) { + + handleTriangle( this, face.a, face.b, face.c ); + + this.vertices[ face.a ].normal.copy( face.vertexNormals[ 0 ] ); + this.vertices[ face.b ].normal.copy( face.vertexNormals[ 1 ] ); + this.vertices[ face.c ].normal.copy( face.vertexNormals[ 2 ] ); + + + } else if ( face instanceof THREE.Face4 ) { + + handleTriangle( this, face.a, face.b, face.c ); + + // this messes up everything + // quads need to be handled differently + //handleTriangle( this, face.a, face.c, face.d ); + + this.vertices[ face.a ].normal.copy( face.vertexNormals[ 0 ] ); + this.vertices[ face.b ].normal.copy( face.vertexNormals[ 1 ] ); + this.vertices[ face.c ].normal.copy( face.vertexNormals[ 2 ] ); + this.vertices[ face.d ].normal.copy( face.vertexNormals[ 3 ] ); + + } + + } + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + n.copy( this.vertices[ v ].normal ); + t = tan1[ v ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.subSelf( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.cross( this.vertices[ v ].normal, t ); + test = tmp2.dot( tan2[ v ] ); + w = (test < 0.0) ? -1.0 : 1.0; + + this.vertices[ v ].tangent.set( tmp.x, tmp.y, tmp.z, w ); + + } + + this.hasTangents = true; + + }, + computeBoundingBox: function () { if ( this.vertices.length > 0 ) { diff --git a/src/core/Vertex.js b/src/core/Vertex.js index 0fab06ee..af45869c 100644 --- a/src/core/Vertex.js +++ b/src/core/Vertex.js @@ -12,6 +12,8 @@ THREE.Vertex = function ( position, normal ) { this.normalWorld = new THREE.Vector3(); this.normalScreen = new THREE.Vector3(); + this.tangent = new THREE.Vector4(); + this.__visible = true; } diff --git a/src/extras/ShaderUtils.js b/src/extras/ShaderUtils.js index a87af227..8f11339d 100644 --- a/src/extras/ShaderUtils.js +++ b/src/extras/ShaderUtils.js @@ -62,6 +62,201 @@ var ShaderUtils = { "}" ].join("\n") + }, + + 'normal' : { + + uniforms: { + + "tNormal": { type: "t", value: 2, texture: null }, + "tAO": { type: "t", value: 3, texture: null }, + + "tDisplacement": { type: "t", value: 4, texture: null }, + "uDisplacementBias": { type: "f", value: -0.5 }, + "uDisplacementScale":{ type: "f", value: 2.5 }, + + "uPointLightPos": { type: "v3", value: new THREE.Vector3() }, + "uPointLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + + "uDirLightPos": { type: "v3", value: new THREE.Vector3() }, + "uDirLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + + "uAmbientLightColor":{ type: "c", value: new THREE.Color( 0x050505 ) }, + + "uDiffuseColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "uSpecularColor": { type: "c", value: new THREE.Color( 0x111111 ) }, + "uAmbientColor": { type: "c", value: new THREE.Color( 0x050505 ) }, + "uShininess": { type: "f", value: 30 } + + }, + + fragment_shader: [ + + "uniform vec3 uDirLightPos;", + "uniform vec3 uDirLightColor;", + + "uniform vec3 uPointLightPos;", + "uniform vec3 uPointLightColor;", + + "uniform vec3 uAmbientColor;", + "uniform vec3 uDiffuseColor;", + "uniform vec3 uSpecularColor;", + "uniform float uShininess;", + + "uniform sampler2D tNormal;", + "uniform sampler2D tAO;", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "varying vec3 vLightWeighting;", + "varying vec3 vPointLightVector;", + "varying vec3 vViewPosition;", + + "void main() {", + + "vec3 normalTex = normalize( texture2D( tNormal, vUv ).xyz * 2.0 - 1.0 );", + "vec3 aoTex = texture2D( tAO, vUv ).xyz;", + + "mat3 tsb = mat3( vTangent, vBinormal, vNormal );", + "vec3 finalNormal = tsb * normalTex;", + + "vec3 normal = normalize( finalNormal );", + "vec3 viewPosition = normalize( vViewPosition );", + + // point light + + "vec4 pointDiffuse = vec4( 0.0, 0.0, 0.0, 0.0 );", + "vec4 pointSpecular = vec4( 0.0, 0.0, 0.0, 0.0 );", + + "vec3 pointVector = normalize( vPointLightVector );", + "vec3 pointHalfVector = normalize( vPointLightVector + vViewPosition );", + + "float pointDotNormalHalf = dot( normal, pointHalfVector );", + "float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", + + "float pointSpecularWeight = 0.0;", + "if ( pointDotNormalHalf >= 0.0 )", + "pointSpecularWeight = pow( pointDotNormalHalf, uShininess );", + + "pointDiffuse += vec4( uDiffuseColor, 1.0 ) * pointDiffuseWeight;", + "pointSpecular += vec4( uSpecularColor, 1.0 ) * pointSpecularWeight;", + + // directional light + + "vec4 dirDiffuse = vec4( 0.0, 0.0, 0.0, 0.0 );", + "vec4 dirSpecular = vec4( 0.0, 0.0, 0.0, 0.0 );", + + "vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );", + + "vec3 dirVector = normalize( lDirection.xyz );", + "vec3 dirHalfVector = normalize( lDirection.xyz + vViewPosition );", + + "float dirDotNormalHalf = dot( normal, dirHalfVector );", + "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + + "float dirSpecularWeight = 0.0;", + "if ( dirDotNormalHalf >= 0.0 )", + "dirSpecularWeight = pow( dirDotNormalHalf, uShininess );", + + "dirDiffuse += vec4( uDiffuseColor, 1.0 ) * dirDiffuseWeight;", + "dirSpecular += vec4( uSpecularColor, 1.0 ) * dirSpecularWeight;", + + // all lights contribution summation + + "vec4 totalLight = vec4( uAmbientColor, 1.0 );", + "totalLight += dirDiffuse + dirSpecular;", + "totalLight += pointDiffuse + pointSpecular;", + + "gl_FragColor = vec4( totalLight.xyz * vLightWeighting * aoTex, 1.0 );", + + "}" + ].join("\n"), + + vertex_shader: [ + + "attribute vec4 tangent;", + + "uniform vec3 uDirLightPos;", + "uniform vec3 uDirLightColor;", + + "uniform vec3 uPointLightPos;", + "uniform vec3 uPointLightColor;", + + "uniform vec3 uAmbientLightColor;", + + "#ifdef VERTEX_TEXTURES", + + "uniform sampler2D tDisplacement;", + "uniform float uDisplacementScale;", + "uniform float uDisplacementBias;", + + "#endif", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "varying vec3 vLightWeighting;", + "varying vec3 vPointLightVector;", + "varying vec3 vViewPosition;", + + "void main() {", + + "vec4 mPosition = objectMatrix * vec4( position, 1.0 );", + "vViewPosition = cameraPosition - mPosition.xyz;", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vNormal = normalize( normalMatrix * normal );", + + // tangent and binormal vectors + + "vTangent = normalize( normalMatrix * tangent.xyz );", + + "vBinormal = cross( vNormal, vTangent ) * tangent.w;", + "vBinormal = normalize( vBinormal );", + + "vUv = uv;", + + // ambient light + + "vLightWeighting = uAmbientLightColor;", + + // point light + + "vec4 lPosition = viewMatrix * vec4( uPointLightPos, 1.0 );", + "vPointLightVector = normalize( lPosition.xyz - mvPosition.xyz );", + "float pointLightWeighting = max( dot( vNormal, vPointLightVector ), 0.0 );", + "vLightWeighting += uPointLightColor * pointLightWeighting;", + + // directional light + + "vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );", + "float directionalLightWeighting = max( dot( vNormal, normalize( lDirection.xyz ) ), 0.0 );", + "vLightWeighting += uDirLightColor * directionalLightWeighting;", + + // displacement mapping + + "#ifdef VERTEX_TEXTURES", + + "vec3 dv = texture2D( tDisplacement, uv ).xyz;", + "float df = uDisplacementScale * dv.x + uDisplacementBias;", + "vec4 displacedPosition = vec4( vNormal.xyz * df, 0.0 ) + mvPosition;", + "gl_Position = projectionMatrix * displacedPosition;", + + "#else", + + "gl_Position = projectionMatrix * mvPosition;", + + "#endif", + + "}" + + ].join("\n") + } } diff --git a/src/materials/MeshShaderMaterial.js b/src/materials/MeshShaderMaterial.js index a7190d3c..1aed66fb 100644 --- a/src/materials/MeshShaderMaterial.js +++ b/src/materials/MeshShaderMaterial.js @@ -21,9 +21,10 @@ THREE.MeshShaderMaterial = function ( parameters ) { this.vertex_shader = "void main() {}"; this.uniforms = {}; + this.opacity = 1; this.shading = THREE.SmoothShading; this.blending = THREE.NormalBlending; - + this.wireframe = false; this.wireframe_linewidth = 1; this.wireframe_linecap = 'round'; diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 7b96432a..452c5533 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -159,13 +159,14 @@ THREE.WebGLRenderer = function ( scene ) { this.createBuffers = function ( object, g ) { - var f, fl, fi, face, vertexNormals, normal, uv, v1, v2, v3, v4, m, ml, i, + var f, fl, fi, face, vertexNormals, normal, uv, v1, v2, v3, v4, t1, t2, t3, t4, m, ml, i, faceArray = [], lineArray = [], vertexArray = [], normalArray = [], + tangentArray = [], uvArray = [], vertexIndex = 0, @@ -193,8 +194,21 @@ THREE.WebGLRenderer = function ( scene ) { vertexArray.push( v2.x, v2.y, v2.z ); vertexArray.push( v3.x, v3.y, v3.z ); + if ( object.geometry.hasTangents ) { + + t1 = object.geometry.vertices[ face.a ].tangent; + t2 = object.geometry.vertices[ face.b ].tangent; + t3 = object.geometry.vertices[ face.c ].tangent; + + tangentArray.push( t1.x, t1.y, t1.z, t1.w ); + tangentArray.push( t2.x, t2.y, t2.z, t2.w ); + tangentArray.push( t3.x, t3.y, t3.z, t3.w ); + + } + if ( vertexNormals.length == 3 && needsSmoothNormals ) { + for ( i = 0; i < 3; i ++ ) { normalArray.push( vertexNormals[ i ].x, vertexNormals[ i ].y, vertexNormals[ i ].z ); @@ -242,15 +256,29 @@ THREE.WebGLRenderer = function ( scene ) { vertexArray.push( v2.x, v2.y, v2.z ); vertexArray.push( v3.x, v3.y, v3.z ); vertexArray.push( v4.x, v4.y, v4.z ); + + if ( object.geometry.hasTangents ) { + + t1 = object.geometry.vertices[ face.a ].tangent; + t2 = object.geometry.vertices[ face.b ].tangent; + t3 = object.geometry.vertices[ face.c ].tangent; + t4 = object.geometry.vertices[ face.d ].tangent; + + tangentArray.push( t1.x, t1.y, t1.z, t1.w ); + tangentArray.push( t2.x, t2.y, t2.z, t2.w ); + tangentArray.push( t3.x, t3.y, t3.z, t3.w ); + tangentArray.push( t4.x, t4.y, t4.z, t4.w ); + + } if ( vertexNormals.length == 4 && needsSmoothNormals ) { - + for ( i = 0; i < 4; i ++ ) { normalArray.push( vertexNormals[ i ].x, vertexNormals[ i ].y, vertexNormals[ i ].z ); } - + } else { for ( i = 0; i < 4; i ++ ) { @@ -302,6 +330,14 @@ THREE.WebGLRenderer = function ( scene ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryChunk.__webGLNormalBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, new Float32Array( normalArray ), _gl.STATIC_DRAW ); + if ( object.geometry.hasTangents ) { + + geometryChunk.__webGLTangentBuffer = _gl.createBuffer(); + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryChunk.__webGLTangentBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, new Float32Array( tangentArray ), _gl.STATIC_DRAW ); + + } + if ( uvArray.length > 0 ) { geometryChunk.__webGLUVBuffer = _gl.createBuffer(); @@ -346,7 +382,7 @@ THREE.WebGLRenderer = function ( scene ) { } cacheUniformLocations( material.program, identifiers ); - cacheAttributeLocations( material.program, [ "position", "normal", "uv" ] ); + cacheAttributeLocations( material.program, [ "position", "normal", "uv", "tangent" ] ); } @@ -374,12 +410,13 @@ THREE.WebGLRenderer = function ( scene ) { this.loadCamera( program, camera ); this.loadMatrices( program ); - if ( material instanceof THREE.MeshShaderMaterial ) { mWireframe = material.wireframe; mLineWidth = material.wireframe_linewidth; - + + mBlending = material.blending; + setUniforms( program, material.uniforms ); } @@ -505,6 +542,15 @@ THREE.WebGLRenderer = function ( scene ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryChunk.__webGLNormalBuffer ); _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + // tangents + + if ( attributes.tangent >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryChunk.__webGLTangentBuffer ); + _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); + + } + // uvs if ( attributes.uv >= 0 ) { @@ -826,6 +872,19 @@ THREE.WebGLRenderer = function ( scene ) { }; + this.supportsVertexTextures = function() { + + return maxVertexTextures() > 0; + + }; + + function maxVertexTextures() { + + return _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + + }; + + function initGL() { try { @@ -888,7 +947,6 @@ THREE.WebGLRenderer = function ( scene ) { "uniform int pointLightNumber;", "uniform int directionalLightNumber;", - maxDirLights ? "uniform mat4 viewMatrix;" : "", maxDirLights ? "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];" : "", "varying vec3 vNormal;", @@ -939,7 +997,7 @@ THREE.WebGLRenderer = function ( scene ) { "} else if ( material == 4 ) { ", - "gl_FragColor = vec4( 0.5*normalize( vNormal ) + vec3(0.5, 0.5, 0.5), mOpacity );", + "gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, mOpacity );", // Depth @@ -1094,9 +1152,6 @@ THREE.WebGLRenderer = function ( scene ) { maxPointLights ? "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];" : "", maxPointLights ? "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];" : "", - "uniform mat4 viewMatrix;", - "uniform mat3 normalMatrix;", - "varying vec3 vNormal;", "varying vec2 vUv;", @@ -1137,7 +1192,7 @@ THREE.WebGLRenderer = function ( scene ) { maxDirLights ? "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {" : "", maxDirLights ? "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );" : "", - maxDirLights ? "float directionalLightWeighting = max( dot( transformedNormal, normalize(lDirection.xyz ) ), 0.0 );" : "", + maxDirLights ? "float directionalLightWeighting = max( dot( transformedNormal, normalize( lDirection.xyz ) ), 0.0 );" : "", maxDirLights ? "vLightWeighting += directionalLightColor[ i ] * directionalLightWeighting;" : "", maxDirLights ? "}" : "", @@ -1181,13 +1236,18 @@ THREE.WebGLRenderer = function ( scene ) { "#ifdef GL_ES", "precision highp float;", "#endif", + "uniform mat4 viewMatrix;", "" ].join("\n"), prefix_vertex = [ + maxVertexTextures() > 0 ? "#define VERTEX_TEXTURES" : "", + "uniform mat4 objectMatrix;", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", + "uniform mat4 viewMatrix;", + "uniform mat3 normalMatrix;", "uniform vec3 cameraPosition;", "attribute vec3 position;", "attribute vec3 normal;", @@ -1231,6 +1291,14 @@ THREE.WebGLRenderer = function ( scene ) { } else if( type == "f" ) { _gl.uniform1f( location, value ); + + } else if( type == "v3" ) { + + _gl.uniform3f( location, value.x, value.y, value.z ); + + } else if( type == "c" ) { + + _gl.uniform3f( location, value.r, value.g, value.b ); } else if( type == "t" ) {