Index: o3djs/effect.js |
=================================================================== |
--- o3djs/effect.js (revision 60893) |
+++ o3djs/effect.js (working copy) |
@@ -466,7 +466,7 @@ |
p.semanticSuffix('TEXCOORD' + |
p.interpolant_++ + '') + ';\n' + |
p.VARYING + p.FLOAT3 + ' ' + |
- p.VARYING_DECLARATION_PREFIX + 'surfaceToLight' + |
+ p.VARYING_DECLARATION_PREFIX + 'surfacePosition' + |
p.semanticSuffix( |
'TEXCOORD' + p.interpolant_++ + '') + ';\n'; |
} |
@@ -836,12 +836,24 @@ |
* |
* @param {!o3d.Material} material Material for which to build the shader. |
* @param {string} effectType Type of effect to create ('phong', 'lambert', |
- * 'constant'). |
+ * 'constant', 'blinn'). |
+ * @param {Object} opt_options Parameters to customize shader code generation. |
+ See o3djs.effect.getStandardShader for details on the possible values. |
* @return {{description: string, shader: string}} A description and the shader |
* string. |
*/ |
o3djs.effect.buildStandardShaderString = function(material, |
- effectType) { |
+ effectType, |
+ opt_options) { |
+ if (!opt_options) { |
+ opt_options = {}; |
+ } |
+ var numLights = 0; |
+ var currentLightWorldPos = 'lightWorldPos'; |
+ if (opt_options.lights) { |
+ numLights = opt_options.lights; |
+ currentLightWorldPos = 'lightWorldPosList[i]'; |
+ } |
var p = o3djs.effect; |
var bumpSampler = material.getParam('bumpSampler'); |
var bumpUVInterpolant; |
@@ -901,9 +913,9 @@ |
* @return {string} The effect code for the common shader uniforms. |
*/ |
var buildCommonVertexUniforms = function() { |
+ //var size = numLights ? '['+numLights+']' : ''; |
return 'uniform ' + p.MATRIX4 + ' worldViewProjection' + |
- p.semanticSuffix('WORLDVIEWPROJECTION') + ';\n' + |
- 'uniform ' + p.FLOAT3 + ' lightWorldPos;\n'; |
+ p.semanticSuffix('WORLDVIEWPROJECTION') + ';\n'; |
}; |
/** |
@@ -911,7 +923,13 @@ |
* @return {string} The effect code for the common shader uniforms. |
*/ |
var buildCommonPixelUniforms = function() { |
- return 'uniform ' + p.FLOAT4 + ' lightColor;\n'; |
+ if (numLights > 0) { |
+ return 'uniform ' + p.FLOAT4 + ' lightColorList[' + numLights + '];\n' + |
+ 'uniform ' + p.FLOAT3 + ' lightWorldPosList[' + numLights + '];\n'; |
+ } else { |
+ return 'uniform ' + p.FLOAT4 + ' lightColor;\n' + |
+ 'uniform ' + p.FLOAT3 + ' lightWorldPos' + ';\n'; |
+ } |
}; |
/** |
@@ -991,6 +1009,37 @@ |
}; |
/** |
+ * Begins a section of code which is to be run once for each light. |
+ * @return {string} The effect code for the for loop, or the empty string if |
+ * not using multiple lights. |
+ */ |
+ var beginLightLoop = function() { |
+ if (numLights) { |
+ return ' ' + p.FLOAT4 + ' lightColorDiffuse = ' + p.FLOAT4 + '(0);\n' + |
+ ' for (int i = 0; i < ' + numLights + '; i++) {\n'; |
+ } else { |
+ return ''; |
+ } |
+ }; |
+ |
+ /** |
+ * Ends the block of code which is to be run for each light. Adds the current |
+ * light's color times (diffuseExpression) into lightColorDiffuse. |
+ * @param {string} diffuseExpression Expression to multiply by light color. |
+ * @return {string} The effect code to set lightColorDiffuse. |
+ */ |
+ var endLightLoop = function(diffuseExpression) { |
+ if (numLights) { |
+ return ' lightColorDiffuse += ' + |
+ 'lightColorList[i] * ( ' + diffuseExpression + ');\n' + |
+ ' }\n'; |
+ } else { |
+ return ' ' + p.FLOAT4 + ' lightColorDiffuse = ' + |
+ 'lightColor * (' + diffuseExpression + ');'; |
+ } |
+ }; |
+ |
+ /** |
* Builds vertex and fragment shader string for the Constant lighting type. |
* @param {!o3d.Material} material The material for which to build |
* shaders. |
@@ -1034,7 +1083,7 @@ |
p.buildUVPassthroughs(material) + |
positionVertexShaderCode() + |
normalVertexShaderCode() + |
- surfaceToLightVertexShaderCode() + |
+ surfacePositionVertexShaderCode() + |
bumpVertexShaderCode() + |
p.endVertexShaderMain() + |
p.pixelShaderHeader(material, true, false) + |
@@ -1050,14 +1099,16 @@ |
getColorParam(material, 'ambient') + |
getColorParam(material, 'diffuse') + |
getNormalShaderCode() + |
- ' ' + p.FLOAT3 + ' surfaceToLight = normalize(' + |
- p.PIXEL_VARYING_PREFIX + 'surfaceToLight);\n' + |
- ' ' + p.FLOAT4 + |
- ' litR = lit(dot(normal, surfaceToLight), 0.0, 0.0);\n' + |
+ beginLightLoop() + |
+ ' ' + p.FLOAT3 + ' surfaceToLight = normalize(' + |
+ currentLightWorldPos + ' - ' + |
+ p.PIXEL_VARYING_PREFIX + 'surfacePosition);\n' + |
+ ' ' + p.FLOAT4 + |
+ ' litR = lit(dot(normal, surfaceToLight), 0.0, 0.0);\n' + |
+ endLightLoop('ambient * diffuse + diffuse * litR.y') + |
p.endPixelShaderMain(p.FLOAT4 + |
'((emissive +\n' + |
- ' lightColor *' + |
- ' (ambient * diffuse + diffuse * litR.y)).rgb,\n' + |
+ ' lightColorDiffuse).rgb,\n' + |
' diffuse.a)') + |
p.entryPoints() + |
p.matrixLoadOrder(); |
@@ -1082,7 +1133,7 @@ |
p.buildUVPassthroughs(material) + |
positionVertexShaderCode() + |
normalVertexShaderCode() + |
- surfaceToLightVertexShaderCode() + |
+ surfacePositionVertexShaderCode() + |
surfaceToViewVertexShaderCode() + |
bumpVertexShaderCode() + |
p.endVertexShaderMain() + |
@@ -1103,22 +1154,21 @@ |
getColorParam(material, 'diffuse') + |
getColorParam(material, 'specular') + |
getNormalShaderCode() + |
- ' ' + p.FLOAT3 + ' surfaceToLight = normalize(' + |
- p.PIXEL_VARYING_PREFIX + 'surfaceToLight);\n' + |
' ' + p.FLOAT3 + ' surfaceToView = normalize(' + |
p.PIXEL_VARYING_PREFIX + 'surfaceToView);\n' + |
- ' ' + p.FLOAT3 + |
- ' halfVector = normalize(surfaceToLight + ' + |
- p.PIXEL_VARYING_PREFIX + 'surfaceToView);\n' + |
- ' ' + p.FLOAT4 + |
+ beginLightLoop() + |
+ ' ' + p.FLOAT3 + ' surfaceToLight = normalize(' + |
+ currentLightWorldPos + ' - ' + |
+ p.PIXEL_VARYING_PREFIX + 'surfacePosition);\n' + |
+ ' ' + p.FLOAT3 + |
+ ' halfVector = normalize(surfaceToLight + surfaceToView);\n' + |
+ ' ' + p.FLOAT4 + |
' litR = lit(dot(normal, surfaceToLight), \n' + |
' dot(normal, halfVector), shininess);\n' + |
+ endLightLoop('ambient * diffuse + diffuse * litR.y\n' + |
+ ' + specular * litR.z * specularFactor') + |
p.endPixelShaderMain( p.FLOAT4 + |
- '((emissive +\n' + |
- ' lightColor *' + |
- ' (ambient * diffuse + diffuse * litR.y +\n' + |
- ' + specular * litR.z *' + |
- ' specularFactor)).rgb,\n' + |
+ '((emissive + lightColorDiffuse).rgb,\n' + |
' diffuse.a)') + |
p.entryPoints() + |
p.matrixLoadOrder(); |
@@ -1141,7 +1191,7 @@ |
p.buildUVPassthroughs(material) + |
positionVertexShaderCode() + |
normalVertexShaderCode() + |
- surfaceToLightVertexShaderCode() + |
+ surfacePositionVertexShaderCode() + |
surfaceToViewVertexShaderCode() + |
bumpVertexShaderCode() + |
p.endVertexShaderMain() + |
@@ -1162,20 +1212,21 @@ |
getColorParam(material, 'diffuse') + |
getColorParam(material, 'specular') + |
getNormalShaderCode() + |
- ' ' + p.FLOAT3 + ' surfaceToLight = normalize(' + |
- p.PIXEL_VARYING_PREFIX + 'surfaceToLight);\n' + |
' ' + p.FLOAT3 + ' surfaceToView = normalize(' + |
p.PIXEL_VARYING_PREFIX + 'surfaceToView);\n' + |
- ' ' + p.FLOAT3 + |
+ beginLightLoop() + |
+ ' ' + p.FLOAT3 + ' surfaceToLight = normalize(' + |
+ currentLightWorldPos + ' - ' + |
+ p.PIXEL_VARYING_PREFIX + 'surfacePosition);\n' + |
+ ' ' + p.FLOAT3 + |
' halfVector = normalize(surfaceToLight + surfaceToView);\n' + |
- ' ' + p.FLOAT4 + |
+ ' ' + p.FLOAT4 + |
' litR = lit(dot(normal, surfaceToLight), \n' + |
' dot(normal, halfVector), shininess);\n' + |
+ endLightLoop('ambient * diffuse + diffuse * litR.y +\n' + |
+ ' + specular * litR.z * specularFactor') + |
p.endPixelShaderMain(p.FLOAT4 + |
- '((emissive +\n' + |
- ' lightColor * (ambient * diffuse + diffuse * litR.y +\n' + |
- ' + specular * litR.z *' + |
- ' specularFactor)).rgb,\n' + |
+ '((emissive + lightColorDiffuse).rgb,\n' + |
' diffuse.a)') + |
p.entryPoints() + |
p.matrixLoadOrder(); |
@@ -1240,13 +1291,14 @@ |
}; |
/** |
- * Builds the surface to light code for the vertex shader. |
+ * Builds the surface position code for the vertex shader. To support |
+ * multiple lights, the dot product with each light should then be |
+ * computed in the fragment shader. |
* @return {string} The code for the vertex shader. |
*/ |
- var surfaceToLightVertexShaderCode = function() { |
+ var surfacePositionVertexShaderCode = function() { |
return ' ' + p.VERTEX_VARYING_PREFIX + |
- 'surfaceToLight = lightWorldPos - \n' + |
- ' ' + |
+ 'surfacePosition = \n' + |
p.mul(p.ATTRIBUTE_PREFIX + 'position', |
'world') + '.xyz;\n'; |
}; |
@@ -1346,14 +1398,19 @@ |
* @param {!o3d.Pack} pack Pack in which to create the new Effect. |
* @param {!o3d.Material} material Material for which to build the shader. |
* @param {string} effectType Type of effect to create ('phong', 'lambert', |
- * 'constant'). |
+ * 'constant', 'blinn'). |
+ * @param {{lights: number}} opt_options Extra options. |
+ * If 'lights' is non-zero, creates an array of light params; otherwise |
+ * only a single light is supported. |
* @return {o3d.Effect} The created effect. |
*/ |
o3djs.effect.getStandardShader = function(pack, |
material, |
- effectType) { |
+ effectType, |
+ opt_options) { |
var record = o3djs.effect.buildStandardShaderString(material, |
- effectType); |
+ effectType, |
+ opt_options); |
var effects = pack.getObjectsByClassName('o3d.Effect'); |
for (var ii = 0; ii < effects.length; ++ii) { |
if (effects[ii].name == record.description && |
@@ -1383,16 +1440,19 @@ |
* @param {!o3d.Material} material Material for which to build the shader. |
* @param {!o3djs.math.Vector3} lightPos Position of the default light. |
* @param {string} effectType Type of effect to create ('phong', 'lambert', |
- * 'constant'). |
+ * 'constant', 'blinn'). |
+ * @param {Object} opt_options Extra options for effect.getStandardShader |
* @return {boolean} True on success. |
*/ |
o3djs.effect.attachStandardShader = function(pack, |
material, |
lightPos, |
- effectType) { |
+ effectType, |
+ opt_options) { |
var effect = o3djs.effect.getStandardShader(pack, |
material, |
- effectType); |
+ effectType, |
+ opt_options); |
if (effect) { |
material.effect = effect; |
effect.createUniformParameters(material); |