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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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" ) {