Cg Programming In Unity Specular Highlights (Wiki翻译自用)


本教程介绍了使用Phong反射模型的每个顶点照明(也称为Gouraud shading)。
它将“Diffuse Reflection”部分中的shader代码扩展成了两个附加术语:环境光照和镜面反射(高光)。这三个术语同时构成了Phong反射模型。

环境光

在这里插入图片描述思考上图画作的左边, 尽管白衬衫的大部分处于阴影中,但没有任何一部分是完全黑色的。显然,总是有一些光从墙壁和其他物体反射而出,以照亮场景中的所有物体-至少在一定程度上。在Phong反射模型中,环境光照考虑了这种影响,环境光照取决于一般的环境光强度 I a m b i e n t I_{ambient} Iambient和用于漫反射的材料颜色 k d i f f u s e k_{diffuse} kdiffuse。 环境照明强度 I a m b i e n t I_{ambient} Iambient的方程式:
*

I a m b i e n t = I a m b i e n t l i g h t ∗ K d i f f u s e I_{ambient}=I_{ambientlight}*K_{diffuse} Iambient=IambientlightKdiffuse

与“漫反射”部分中的漫反射方程类似,该方程也可以解释为光的红色,绿色和蓝色分量的矢量方程。
在unity中,一个均匀的环境光可以通过主菜单选择Window > Rendering > Lighting Settings设置Scene > Environment Lighting > SourceColor以指定环境光。在Unity的Cg着色器中,此颜色将以UNITY_LIGHTMODEL_AMBIENT提供。(如果选择的“Gradient”而不是“Color”,那么UNITY_LIGHTMODEL_AMBIENTunity_AmbientSky 指定天空颜色,赤道颜色由unity_AmbientEquator指定,地面颜色由unity_AmbientGround指定)

镜面高光

如果您仔细观察上面的油画,你会看到一些镜面高光在:鼻子,头发,嘴唇,琵琶,小提琴,弓,水果等上面。Phong反射模型包含了镜面反射项,可以模拟发亮表面上的此类高光;它还包括参数 n s h i n n i n e s s n_{shinniness} nshinniness用以指定材质的光泽度。光泽度指定高光大小:光泽度越高,高光越小。
一个完美的发光表面只会在几何反射方向R上反射来自光源的光。对于不太完美发亮的表面,光会反射到R周围的方向:光泽度越小,散布就越广,高光越大,镜面反射效果越不明显。在数学上,对于归一化的表面法线向量N和归一化光源方向L,归一化的反射方向R的定义方程式为:
*

R = 2 N ( N ⋅ L ) − L R=2N(N·L)-L R=2N(NL)L

在Cg中,函数float3 reflect(float3 I, float3 N)float4 reflect(float4 I, float4 N)计算上式相同的反射矢量,但是方向I是从光源到曲面上的点的方向。因此,我们必须使用方向L的反方向,才能使用此函数。
镜面反射项计算在观察方向V看到的镜面反射。如上所述,如果V接近R,则镜面反射强度较大,其中接近程度由光泽度 n s h i n i n e s s n_{shininess} nshininess参数化。在Phong反射模型中,RV余弦值 的 n s h i n i n e s s n_{shininess} nshininess幂用于生成不同光泽度的高光。与漫反射的情况类似,我们应将余弦值小于0的结果固定在0。此外,镜面反射要求镜面反射的材质颜色为 k s p e c u l a r k_{specular} kspecular,通常为白色,以便所有高光都具有入射光 I i n c o m i n g I_{incoming} Iincoming的颜色。例如,上图的油画中,所有高光都是白色的。那么,Phong反射模型的镜面反射项为:
*

I s p e c u l a r = I i n c o m i n g k s p e c u l a r m a x ( 0 , R ⋅ V ) n s h i n i n e s s I_{specular}=I_{incoming}k_{specular}max(0,R·V)^{n_{shininess}} Ispecular=Iincomingkspecularmax(0,RV)nshininess

与漫反射情况类似,如果光源在表面的相反一侧,则应忽略镜面反射项, 即,如果点积N·L为负。

shader代码

环境光照的着色器代码非常简单,使用逐分量矢量的向量积:

float3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb; 

为了实现镜面反射,我们需要我们需要在世界空间中定义指向观察者的方向。我们可以将其计算为相机位置和顶点位置的差。相机在世界空间中的位置由Unity内置参数_WorldSpaceCameraPos提供。然后可以像下面这样实现在世界空间中的镜面反射公式:

float3 viewDir = _WorldSpaceCameraPos - mul(unity_ObjectToWorld,input.vertex).xyz;
float3 specularReflection;
if(dot(normalDir,lightDir) < 0.0)
{
	I_specular = float3(0,0,0);
}
else
{
	I_specular = attenuation * _LightColor0.rgb * _SpecColor.rgb * pow(max(0,
	dot(reflect(-lightDirection, normalDirection),viewDir)),_Shininess);
}

该代码使用了与“漫反射”部分着色器代码相同的变量,以及用户指定的属性SpecColor_Shininesspow(a,b)计算 a b a^b ab
如果将环境光照添加到第一个Pass,并且将镜面反射添加到“漫反射”部分的着色器的两个Pass中,则代码如下:

Shader "Cg per-vertex lighting" {
   Properties {
      _Color ("Diffuse Material Color", Color) = (1,1,1,1) 
      _SpecColor ("Specular Material Color", Color) = (1,1,1,1) 
      _Shininess ("Shininess", Float) = 10
   }
   SubShader {
      Pass {	
         Tags { "LightMode" = "ForwardBase" } 
            // pass for ambient light and first light source
 
         CGPROGRAM
 
         #pragma vertex vert  
         #pragma fragment frag 
 
         #include "UnityCG.cginc"
         uniform float4 _LightColor0; 
            // color of light source (from "Lighting.cginc")
 
         // User-specified properties
         uniform float4 _Color; 
         uniform float4 _SpecColor; 
         uniform float _Shininess;
 
         struct vertexInput {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : COLOR;
         };
 
         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;
 
            float4x4 modelMatrix = unity_ObjectToWorld;
            float3x3 modelMatrixInverse = unity_WorldToObject;
            float3 normalDirection = normalize(
               mul(input.normal, modelMatrixInverse));
            float3 viewDirection = normalize(_WorldSpaceCameraPos 
               - mul(modelMatrix, input.vertex).xyz);
            float3 lightDirection;
            float attenuation;
 
            if (0.0 == _WorldSpaceLightPos0.w) // directional light?
            {
               attenuation = 1.0; // no attenuation
               lightDirection = normalize(_WorldSpaceLightPos0.xyz);
            } 
            else // point or spot light
            {
               float3 vertexToLightSource = _WorldSpaceLightPos0.xyz
                  - mul(modelMatrix, input.vertex).xyz;
               float distance = length(vertexToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(vertexToLightSource);
            }
 
            float3 ambientLighting = 
               UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;
 
            float3 diffuseReflection = 
               attenuation * _LightColor0.rgb * _Color.rgb
               * max(0.0, dot(normalDirection, lightDirection));
 
            float3 specularReflection;
            if (dot(normalDirection, lightDirection) < 0.0) 
               // light source on the wrong side?
            {
               specularReflection = float3(0.0, 0.0, 0.0); 
                  // no specular reflection
            }
            else // light source on the right side
            {
               specularReflection = attenuation * _LightColor0.rgb 
                  * _SpecColor.rgb * pow(max(0.0, dot(
                  reflect(-lightDirection, normalDirection), 
                  viewDirection)), _Shininess);
            }
 
            output.col = float4(ambientLighting + diffuseReflection 
               + specularReflection, 1.0);
            output.pos = UnityObjectToClipPos(input.vertex);
            return output;
         }
 
         float4 frag(vertexOutput input) : COLOR
         {
            return input.col;
         }
 
         ENDCG
      }
 
      Pass {	
         Tags { "LightMode" = "ForwardAdd" } 
            // pass for additional light sources
         Blend One One // additive blending 
 
         CGPROGRAM
 
         #pragma vertex vert  
         #pragma fragment frag 
 
         #include "UnityCG.cginc"
         uniform float4 _LightColor0; 
            // color of light source (from "Lighting.cginc")
 
         // User-specified properties
         uniform float4 _Color; 
         uniform float4 _SpecColor; 
         uniform float _Shininess;
 
         struct vertexInput {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : COLOR;
         };
 
         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;
 
            float4x4 modelMatrix = unity_ObjectToWorld;
            float3x3 modelMatrixInverse = unity_WorldToObject;
            float3 normalDirection = normalize(
               mul(input.normal, modelMatrixInverse));
            float3 viewDirection = normalize(_WorldSpaceCameraPos 
               - mul(modelMatrix, input.vertex).xyz);
            float3 lightDirection;
            float attenuation;
 
            if (0.0 == _WorldSpaceLightPos0.w) // directional light?
            {
               attenuation = 1.0; // no attenuation
               lightDirection = normalize(_WorldSpaceLightPos0.xyz);
            } 
            else // point or spot light
            {
               float3 vertexToLightSource = _WorldSpaceLightPos0.xyz
                  - mul(modelMatrix, input.vertex).xyz;
               float distance = length(vertexToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(vertexToLightSource);
            }
 
            float3 diffuseReflection = 
               attenuation * _LightColor0.rgb * _Color.rgb
               * max(0.0, dot(normalDirection, lightDirection));
 
            float3 specularReflection;
            if (dot(normalDirection, lightDirection) < 0.0) 
               // light source on the wrong side?
            {
               specularReflection = float3(0.0, 0.0, 0.0); 
                  // no specular reflection
            }
            else // light source on the right side
            {
               specularReflection = attenuation * _LightColor0.rgb 
                  * _SpecColor.rgb * pow(max(0.0, dot(
                  reflect(-lightDirection, normalDirection), 
                  viewDirection)), _Shininess);
            }
 
            output.col = float4(diffuseReflection 
               + specularReflection, 1.0);
               // no ambient contribution in this pass
            output.pos = UnityObjectToClipPos(input.vertex);
            return output;
         }
 
         float4 frag(vertexOutput input) : COLOR
         {
            return input.col;
         }
 
         ENDCG
      }
   }
   Fallback "Specular"
}

总结

恭喜,您刚刚学习了如何实现Phong反射模型。我们已经学会了:

  • Phong反射模型中的环境照明是什么。(一般的环境光强度 I a m b i e n t I_{ambient} Iambient和用于漫反射的材料颜色 k d i f f u s e k_{diffuse} kdiffuse
  • Phong反射模型中的镜面反射项是什么。( I s p e c u l a r = I i n c o m i n g k s p e c u l a r m a x ( 0 , R ⋅ V ) n s h i n i n e s s I_{specular}=I_{incoming}k_{specular}max(0,R·V)^{n_{shininess}} Ispecular=Iincomingkspecularmax(0,RV)nshininess
  • 这些术语如何在Unity中的Cg中实现。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值