Movatterモバイル変換


[0]ホーム

URL:


Jump to content
WikibooksThe Free Textbook Project
Search

Cg Programming/Unity/Specular Highlights

From Wikibooks, open books for an open world
<Cg Programming |Unity
“Apollo the Lute Player” (Badminton House version) by Michelangelo Merisi da Caravaggio, ca. 1596.

This tutorial coversper-vertex lighting (also known asGouraud shading) using thePhong reflection model.

It extends the shader code inSection “Diffuse Reflection” by two additional terms: ambient lighting and specular reflection. Together, the three terms constitute the Phong reflection model. If you haven't readSection “Diffuse Reflection”, this would be a very good opportunity to read it.

Ambient Light

[edit |edit source]

Consider the painting by Caravaggio to the left. While large parts of the white shirt are in shadows, no part of it is completely black. Apparently there is always some light being reflected from walls and other objects to illuminate everything in the scene — at least to a certain degree. In the Phong reflection model, this effect is taken into account by ambient lighting, which depends on a general ambient light intensityIambient light{\displaystyle I_{\text{ambient light}}} and the material colorkdiffuse{\displaystyle k_{\text{diffuse}}} for diffuse reflection. In an equation for the intensity of ambient lightingIambient{\displaystyle I_{\text{ambient}}}:

Iambient=Iambient lightkdiffuse{\displaystyle I_{\text{ambient}}=I_{\text{ambient light}}\,k_{\text{diffuse}}}

Analogously to the equation for diffuse reflection inSection “Diffuse Reflection”, this equation can also be interpreted as a vector equation for the red, green, and blue components of light.

In Unity, a uniform ambient light is specified by choosingWindow > Rendering > Lighting Settings from the main menu, settingScene > Environment Lighting > Source toColor and specifying theAmbient Color. In a Cg shader in Unity, this color is then available asUNITY_LIGHTMODEL_AMBIENT, which is one of the pre-defined uniforms mentioned inSection “Shading in World Space”. (If you chooseGradient instead ofColor thenUNITY_LIGHTMODEL_AMBIENT andunity_AmbientSky specify theSky Color, while theEquator Color and theGround Color are specified byunity_AmbientEquator andunity_AmbientGround.)

The computation of the specular reflection requires the surface normal vector N, the direction to the light source L, the reflected direction to the light source R, and the direction to the viewer V.

Specular Highlights

[edit |edit source]

If you have a closer look at Caravaggio's painting, you will see several specular highlights: on the nose, on the hair, on the lips, on the lute, on the violin, on the bow, on the fruits, etc. The Phong reflection model includes a specular reflection term that can simulate such highlights on shiny surfaces; it even includes a parameternshininess{\displaystyle n_{\text{shininess}}} to specify a shininess of the material. The shininess specifies how small the highlights are: the shinier, the smaller the highlights.

A perfectly shiny surface will reflect light from the light source only in the geometrically reflected directionR. For less than perfectly shiny surfaces, light is reflected to directions aroundR: the smaller the shininess, the wider the spreading. Mathematically, the normalized reflected directionR is defined by:

R=2N(NL)L{\displaystyle \mathbf {R} =2\mathbf {N} (\mathbf {N} \cdot \mathbf {L} )-\mathbf {L} }

for a normalized surface normal vectorN and a normalized direction to the light sourceL. In Cg, the functionfloat3 reflect(float3 I, float3 N) (orfloat4 reflect(float4 I, float4 N)) computes the same reflected vector but for the directionI from the light source to the point on the surface. Thus, we have to negate our directionL to use this function.

The specular reflection term computes the specular reflection in the direction of the viewerV. As discussed above, the intensity should be large ifV is close toR, where “closeness” is parametrized by the shininessnshininess{\displaystyle n_{\text{shininess}}}. In the Phong reflection model, the cosine of the angle betweenR andV to thenshininess{\displaystyle n_{\text{shininess}}}-th power is used to generate highlights of different shininess. Similarly to the case of thediffuse reflection, we should clamp negative cosines to 0. Furthermore, the specular term requires a material colorkspecular{\displaystyle k_{\text{specular}}} for the specular reflection, which is usually just white such that all highlights have the color of the incoming lightIincoming{\displaystyle I_{\text{incoming}}}. For example, all highlights in Caravaggio's painting are white. The specular term of the Phong reflection model is then:

Ispecular=Iincomingkspecularmax(0,RV)nshininess{\displaystyle I_{\text{specular}}=I_{\text{incoming}}\,k_{\text{specular}}\max(0,\mathbf {R} \cdot \mathbf {V} )^{n_{\text{shininess}}}}

Analogously to the case of thediffuse reflection, the specular term should be ignored if the light source is on the “wrong” side of the surface; i.e., if the dot productN·L is negative.

Shader Code

[edit |edit source]

The shader code for the ambient lighting is straightforward with a component-wise vector-vector product:

float3ambientLighting=UNITY_LIGHTMODEL_AMBIENT.rgb*_Color.rgb;

For the implementation of the specular reflection, we require the direction to the viewer in world space, which we can compute as the difference between the camera position and the vertex position (both in world space). The camera position in world space is provided by Unity in the uniform_WorldSpaceCameraPos; the vertex position can be transformed to world space as discussed inSection “Diffuse Reflection”. The equation of the specular term in world space could then be implemented like this:

float3viewDirection=normalize(_WorldSpaceCameraPos-mul(modelMatrix,input.vertex).xyz);float3specularReflection;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);}

This code snippet uses the same variables as the shader code inSection “Diffuse Reflection” and additionally the user-specified properties_SpecColor and_Shininess. (The names were specifically chosen such that the fallback shader can access them; see the discussion inSection “Diffuse Reflection”.)pow(a, b) computesab{\displaystyle a^{b}}.

If the ambient lighting is added to the first pass (we only need it once) and the specular reflection is added to both passes of the full shader ofSection “Diffuse Reflection”, it looks like this:

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 sourceCGPROGRAM#pragma vertex vert#pragma fragment frag#include"UnityCG.cginc"uniformfloat4_LightColor0;// color of light source (from "Lighting.cginc")// User-specified propertiesuniformfloat4_Color;uniformfloat4_SpecColor;uniformfloat_Shininess;structvertexInput{float4vertex:POSITION;float3normal:NORMAL;};structvertexOutput{float4pos:SV_POSITION;float4col:COLOR;};vertexOutputvert(vertexInputinput){vertexOutputoutput;float4x4modelMatrix=unity_ObjectToWorld;float3x3modelMatrixInverse=unity_WorldToObject;float3normalDirection=normalize(mul(input.normal,modelMatrixInverse));float3viewDirection=normalize(_WorldSpaceCameraPos-mul(modelMatrix,input.vertex).xyz);float3lightDirection;floatattenuation;if(0.0==_WorldSpaceLightPos0.w)// directional light?{attenuation=1.0;// no attenuationlightDirection=normalize(_WorldSpaceLightPos0.xyz);}else// point or spot light{float3vertexToLightSource=_WorldSpaceLightPos0.xyz-mul(modelMatrix,input.vertex).xyz;floatdistance=length(vertexToLightSource);attenuation=1.0/distance;// linear attenuationlightDirection=normalize(vertexToLightSource);}float3ambientLighting=UNITY_LIGHTMODEL_AMBIENT.rgb*_Color.rgb;float3diffuseReflection=attenuation*_LightColor0.rgb*_Color.rgb*max(0.0,dot(normalDirection,lightDirection));float3specularReflection;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);returnoutput;}float4frag(vertexOutputinput):COLOR{returninput.col;}ENDCG}Pass{Tags{"LightMode"="ForwardAdd"}// pass for additional light sourcesBlendOneOne// additive blendingCGPROGRAM#pragma vertex vert#pragma fragment frag#include"UnityCG.cginc"uniformfloat4_LightColor0;// color of light source (from "Lighting.cginc")// User-specified propertiesuniformfloat4_Color;uniformfloat4_SpecColor;uniformfloat_Shininess;structvertexInput{float4vertex:POSITION;float3normal:NORMAL;};structvertexOutput{float4pos:SV_POSITION;float4col:COLOR;};vertexOutputvert(vertexInputinput){vertexOutputoutput;float4x4modelMatrix=unity_ObjectToWorld;float3x3modelMatrixInverse=unity_WorldToObject;float3normalDirection=normalize(mul(input.normal,modelMatrixInverse));float3viewDirection=normalize(_WorldSpaceCameraPos-mul(modelMatrix,input.vertex).xyz);float3lightDirection;floatattenuation;if(0.0==_WorldSpaceLightPos0.w)// directional light?{attenuation=1.0;// no attenuationlightDirection=normalize(_WorldSpaceLightPos0.xyz);}else// point or spot light{float3vertexToLightSource=_WorldSpaceLightPos0.xyz-mul(modelMatrix,input.vertex).xyz;floatdistance=length(vertexToLightSource);attenuation=1.0/distance;// linear attenuationlightDirection=normalize(vertexToLightSource);}float3diffuseReflection=attenuation*_LightColor0.rgb*_Color.rgb*max(0.0,dot(normalDirection,lightDirection));float3specularReflection;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 passoutput.pos=UnityObjectToClipPos(input.vertex);returnoutput;}float4frag(vertexOutputinput):COLOR{returninput.col;}ENDCG}}Fallback"Specular"}

Summary

[edit |edit source]

Congratulations, you just learned how to implement the Phong reflection model. In particular, we have seen:

  • What the ambient lighting in the Phong reflection model is.
  • What the specular reflection term in the Phong reflection model is.
  • How these terms can be implemented in Cg in Unity.

Further reading

[edit |edit source]

If you still want to know more

<Cg Programming/Unity

Unless stated otherwise, all example source code on this page is granted to the public domain.
Retrieved from "https://en.wikibooks.org/w/index.php?title=Cg_Programming/Unity/Specular_Highlights&oldid=3714814"
Category:

[8]ページ先頭

©2009-2025 Movatter.jp