转自http://www.itnose.net/detail/6117338.html
前文中完成最简单的漫反射shader只是单个光源下的漫反射,而往往场景中不仅仅只有一个光源,那么多个光源的情况下我们的物体表面的漫反射强度如何叠加在一起呢?前文打的tag "LightMode"="ForwardBase"又是什么意思呢?
Unity内置的DiffuseShader,也就是我们创建一个Material出来时默认的Shader也是多光源的,所以这篇文章完成的shader与默认的diffuse shader基本效果一致。
首先引入几个概念
渲染路径 Rendering Path
像素光 Pixel Light
关于像素光的叠加原理
Blend One One
Shader "Custom/Multi-Light Diffuse" { Properties { //材料颜色默认为黑色,可在inspector中调节 _Color ("Material Color", Color) = (1,1,1,1) } SubShader { Pass{ //第一个像素光所在的反射通道标记为ForwardBase Tags {"LightMode" = "ForwardBase"} CGPROGRAM // Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it does not contain a surface program or both vertex and fragment programs. //#pragma exclude_renderers gles //定义顶点着色器与片段着色器入口 #pragma vertex vert #pragma fragment frag //获取property中定义的材料颜色 uniform float4 _Color; // 光源的位置或者方向 //uniform float4 _WorldSpaceLightPos0; // 光源的颜色 (from "Lighting.cginc") uniform float4 _LightColor0; //定义顶点着色器的输入参数结构体 //我们只需要每个顶点的位置与对应的法向量 struct vertexInput { float4 vertex : POSITION; float3 normal : NORMAL; }; //定义顶点着色的输出结构体/片段着色的输入结构体 //已经计算好的颜色 struct vertexOutput { float4 pos : SV_POSITION; float4 col : COLOR; }; //顶点着色器 vertexOutput vert (vertexInput input) { vertexOutput output; //对象坐标系到世界坐标系的变换矩阵 //_Object2World与_World2Object均为unity提供的内置uniform参数 float4x4 modelMatrix = _Object2World; //世界坐标系到对象坐标系的变换矩阵 float4x4 modelMatrixInverse = _World2Object; //计算对象坐标系中的顶点法向量的单位向量 //将mesh传递过来的顶点法向量与模型-->对象坐标系矩阵相乘得到对象坐标系中的法向量 //然后单位化 float3 normalDirection = normalize(float3(mul(float4(input.normal, 0.0), modelMatrixInverse))); //计算入射向量的单位向量 float3 lightDirection = normalize(float3(_WorldSpaceLightPos0)); //计算反射后的颜色 //先将光源颜色与材料颜色向量相乘 //再乘以上文提到的max(0,cos∠(N,L)) float3 diffuseReflection=float3(_LightColor0) * float3(_Color)* max(0.0, dot(normalDirection, lightDirection)); //上面计算的是RGB颜色,差个A,补充一维就可以传递给片段着色器了 output.col=float4(diffuseReflection,1); //国际惯例,顶点变化三步曲,这个例子中可写可不写 output.pos = mul(UNITY_MATRIX_MVP, input.vertex); return output; } //片段着色器,老规矩,把顶点着色器的输出参数作为片段着色器的输入参数 float4 frag(vertexOutput input): COLOR { return input.col; } ENDCG } Pass{ //其余的像素光所在的反射通道标记为ForwardAdd Tags {"LightMode" = "ForwardAdd"} //此通道的片段着色器输出颜色时帧缓存已经有了颜色,所以需要混合 //混合模式为1比1 Blend One One CGPROGRAM // Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it does not contain a surface program or both vertex and fragment programs. //#pragma exclude_renderers gles //定义顶点着色器与片段着色器入口 #pragma vertex vert #pragma fragment frag //获取property中定义的材料颜色 uniform float4 _Color; // 光源的位置或者方向 //uniform float4 _WorldSpaceLightPos0; // 光源的颜色 (from "Lighting.cginc") uniform float4 _LightColor0; //定义顶点着色器的输入参数结构体 //我们只需要每个顶点的位置与对应的法向量 struct vertexInput { float4 vertex : POSITION; float3 normal : NORMAL; }; //定义顶点着色的输出结构体/片段着色的输入结构体 //已经计算好的颜色 struct vertexOutput { float4 pos : SV_POSITION; float4 col : COLOR; }; //顶点着色器 vertexOutput vert (vertexInput input) { vertexOutput output; //对象坐标系到世界坐标系的变换矩阵 //_Object2World与_World2Object均为unity提供的内置uniform参数 float4x4 modelMatrix = _Object2World; //世界坐标系到对象坐标系的变换矩阵 float4x4 modelMatrixInverse = _World2Object; //计算对象坐标系中的顶点法向量的单位向量 //将mesh传递过来的顶点法向量与模型-->对象坐标系矩阵相乘得到对象坐标系中的法向量 //然后单位化 float3 normalDirection = normalize(float3(mul(float4(input.normal, 0.0), modelMatrixInverse))); //计算入射向量的单位向量 float3 lightDirection = normalize(float3(_WorldSpaceLightPos0)); //计算反射后的颜色 //先将光源颜色与材料颜色向量相乘 //再乘以上文提到的max(0,cos∠(N,L)) float3 diffuseReflection=float3(_LightColor0) * float3(_Color)* max(0.0, dot(normalDirection, lightDirection)); //上面计算的是RGB颜色,差个A,补充一维就可以传递给片段着色器了 output.col=float4(diffuseReflection,1); //国际惯例,顶点变化三步曲,这个例子中可写可不写 output.pos = mul(UNITY_MATRIX_MVP, input.vertex); return output; } //片段着色器,老规矩,把顶点着色器的输出参数作为片段着色器的输入参数 float4 frag(vertexOutput input): COLOR { return input.col; } ENDCG } } }