UnityShader官方案例讲解——SurfaceShader(4)
2017-03-24 01:09
501 查看
“Without purpose, the days would have ended, as such days always end, in disintegration.”
– , Explorer
step1
经世界坐标空间位置的切片
最后一段对UnpackNormal的解释大家不理解也没关系,不影响接下来的学习,循序渐进嘛。
还是上一个官方的效果图吧
step2
使用顶点修改器进行法线挤压
沿法线移动顶点使游戏物体变得臃肿
沿着法线的方向移动顶点的位置,可以想象,其实就相当于把整个模型表面的顶点沿着法线方向“拉长了一截”,,,,而法线是垂直于各自的三角面的,,,所以,,,,它就“胖了”
上效果图,能看出“肿”就可以了
step3
逐顶点计算的自定义数据
效果图
step4
最终颜色修改器 (Final Color Modifier)
这是一个将色调应用于最终颜色的简单着色器。这与仅将色调应用于表面反射率 (Albedo) 颜色不同:此色调也会影响来自光照贴图、光探头和类似额外来源的任何颜色。好厉害的赶脚?
其实我们还可以在mycolor函数中大做文章,让效果更绚
step5
使用最终颜色修改器自定义雾
最终颜色修改器 (Final Color Modifier)(见上文)的常见应用为实现完全自定义的雾 (Fog)。雾 (Fog) 需要影响最终计算出的像素着色器颜色,这恰恰是(Final Color Modifier)修改器的用处所在。
step6
线性雾 (Linear Fog)
从这几篇开始,我已经开始慢慢的放弃一些代码的注释了,原因呢(实在是直白的就略过了)
本系列结束了,都是些简单到不能再简单的东西,感觉就像是复习了一遍3D数学基础,和API
接下来会进行官网的其他系列翻译,我在发表之前会吸收很多前辈的经验,当然肯定有理解的不够深刻的地方,欢迎指正,共同学习。
所有的我参考过的博客都会放第二个文章里
– , Explorer
step1
经世界坐标空间位置的切片
Shader"Example / AutisticPatient SurfaceShader8" { Properties { _MainTex("MainTex",2D) = "white"{} _Bump("Bump",2D) = "Bump"{} } SubShader { Tags{"RenderType" = "Opaque"} CGPROGRAM #pragma surface surf Lambert //cull off是关闭阴影面剔除 //这里说一下Culling指令:culling是阴影面剔除的一种优化技术 //通常情况下,所有的多边形都有正反两面,而背面往往是摄像机看不见的,所以会将看不见的那面剔除掉 //cull back : 剔除背面 //cull front : 剔除正面 Cull off struct Input { float2 uv_MainTex; float2 uv_Bump; //世界空间位置,是个三维向量 float3 worldPos; } sampler2D _MainTex; sampler2D _Bump; void surf(Input IN,inout SurfaceOutput o) { //clip(x) : 如果输入向量中的任何元素小于0,则丢弃当前像素。 //frac() 得作用很简单 ,举个例子:在frac函数内计算出的值若为1.5,frac函数将会取出0.5作为最终值输出 //也就是说frac里的计算结果如果小于0.5,那么这个像素点就被丢弃了 clip(frac( (IN.worldPos.y + IN.worldPos.z * 0.1) * 5) - 0.5); //着色器会找到贴图上对应的UV坐标点,直接使用这个点的颜色信息rgb来进行着色. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; //这里放弃以前的通俗解释 //UnpackNormal是标准法线解压函数,直接看源码 o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } //这里打开UnityCG.cginc,找到这个函数 //下面这些解释都是风宇冲前辈的经验之谈,想看更详细的内容百度:风宇冲 inline fixed3 UnpackNormal(fixed4 packednormal) { //该函数进行了预定义,如果是移动平台或者OpenGL ES,那么断定使用的是RGB法线贴图,否则则为DXT5nm贴图。 //但实际上移动平台也可以用压缩格式的法线贴图,而Windows也能使用RGB法线贴图。 #if defined(SHADER_API_GLES) && defined(SHADER_API_MOBILE) return packednormal.xyz * 2 - 1; #else fixed3 normal; normal.xy = packednormal.wy * 2 - 1; normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y); return normal; #endif } ENDCG } }
最后一段对UnpackNormal的解释大家不理解也没关系,不影响接下来的学习,循序渐进嘛。
还是上一个官方的效果图吧
step2
使用顶点修改器进行法线挤压
沿法线移动顶点使游戏物体变得臃肿
沿着法线的方向移动顶点的位置,可以想象,其实就相当于把整个模型表面的顶点沿着法线方向“拉长了一截”,,,,而法线是垂直于各自的三角面的,,,所以,,,,它就“胖了”
Shader"Example / AutisticPatient SurfaceShader9" { Properties { _MainTex ("Texture", 2D) = "white" {} //range是在Inspector面板中定义了一个滑动条,用于面板控制,不需要对代码进行修改 _Amount ("Extrusion Amount", Range(-1,1)) = 0.5 } SubShader { //子着色器标签,渲染非透明物体 Tags { "RenderType" = "Opaque" } CGPROGRAM //定义表面着色器surf,光照模型Lambert , 顶点着色器vert #pragma surface surf Lambert vertex:vert struct Input { float2 uv_MainTex; }; float _Amount; //顶点着色器,vertex shader的函数名必须和#pragma 编译指令下的名称一致,否则shader找不到入口 void vert (inout appdata_full v) { //appdata_full 输入了一个顶点,并将顶点和当前的法线进行重叠,最后用float值进行倍增。 //还不理解的话建议拆开来看,当然要遵循运算符优先法则 //物体的法线 * float值 进行扩大 //然后顶点的xyz坐标与扩大后的法线进行叠加,顶点的xyz就会在法线的方向基础上进行不断的++ v.vertex.xyz += v.normal * _Amount; } sampler2D _MainTex; //表面着色器 void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
上效果图,能看出“肿”就可以了
step3
逐顶点计算的自定义数据
Shader"Example / AutisticPatient SurfaceShader10" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert vertex:vert struct Input { float2 uv_MainTex; float3 customColor; }; //在这顶点函数内,Input结构做了输出,在表面着色器内做了输入 //以本函数为例,o.customColor输入进来并做了修改,然后作为输入传进了surf函数 void vert (inout appdata_full v, out Input o) { //用Unity内置的宏初始化参数 UNITY_INITIALIZE_OUTPUT(Input,o); //将法线的绝对值赋值给输出结构体下的customColor o.customColor = abs(v.normal); } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { ///获取纹理的UV坐标值,并将颜色值作为最终输出赋给标准输出结构体的Albedo颜色属性 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; //这里Albedo颜色属性进行了倍增 //现在的Input结构体力的customColor不在是最初的那个啥都没有的float3了 //这里的customColor是经过顶点函数处理过的,它现在是abs(v.Normal),这么说理解了吧 o.Albedo *= IN.customColor; } ENDCG } Fallback "Diffuse" }
效果图
step4
最终颜色修改器 (Final Color Modifier)
这是一个将色调应用于最终颜色的简单着色器。这与仅将色调应用于表面反射率 (Albedo) 颜色不同:此色调也会影响来自光照贴图、光探头和类似额外来源的任何颜色。好厉害的赶脚?
Shader"Example / AutisticPatient SurfaceShader11" { Properties { _MainTex ("Texture", 2D) = "white" {} _ColorTint ("Tint", Color) = (1.0, 0.6, 0.6, 1.0) } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM //最终颜色修改器(Final Color ) 函数,该函数将修改由着色器计算的最终颜色。使用着色器编译指令 #pragma surface surf Lambert finalcolor:mycolor struct Input { float2 uv_MainTex; }; fixed4 _ColorTint; //与编译指令对应,名字必须一致 void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { //color值会因受到_ColorTint值的改变而改变 //也就是说我们可以在Inspector面板中进行调试 color *= _ColorTint; } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
其实我们还可以在mycolor函数中大做文章,让效果更绚
step5
使用最终颜色修改器自定义雾
最终颜色修改器 (Final Color Modifier)(见上文)的常见应用为实现完全自定义的雾 (Fog)。雾 (Fog) 需要影响最终计算出的像素着色器颜色,这恰恰是(Final Color Modifier)修改器的用处所在。
Shader"Example / AutisticPatient SurfaceShader12" { //定义需要的属性 Properties { _MainTex ("Texture", 2D) = "white" {} _FogColor ("Fog Color", Color) = (0.3, 0.4, 0.7, 1.0) } SubShader { //渲染非透明物体,最近这些小的shader对标签的要求没那么严苛,以后会接触到更多的标签 Tags { "RenderType" = "Opaque" } CGPROGRAM //编译指令,指定表面着色器,光照模型,最终颜色修改器,和顶点着色器 #pragma surface surf Lambert finalcolor:mycolor vertex:myvert struct Input { float2 uv_MainTex; half fog; }; void myvert (inout appdata_full v, out Input data) { //主要是将叫[data]的变量清空改成Input类型。 //initialize的中文含义便是初始化,试着理解 UNITY_INITIALIZE_OUTPUT(Input,data); //mul,矩阵相乘运算函数,不需要开发者自己手动去进行矩阵相乘,有兴趣的同学可以自己实现一下这个功能 //UNITY_MATRIX_MVP是model、view、projection三个矩阵相乘出来的4x4的矩阵。 //v.vertex是一个float4的变量,可理解成4x1的矩阵(其实可以称作向量),两者相乘,则得出一个float4,这个值就是视角窗口的坐标值了 float4 hpos = mul (UNITY_MATRIX_MVP, v.vertex); //这个fog的浮点值就是其强度,值越小就fog就越黑 //再看后面这个点积,点积运算公式:|x||y|cos(夹角) //这个仔细一想,不难理解,其实就是为了达到一种扩散的效果,因此两个一样的向量相乘(因为cos值为1),其实就是直接对坐标做平方扩展,这样fog就更有雾的感觉。 data.fog = min (1, dot (hpos.xy, hpos.xy) * 0.1); } fixed4 _FogColor; void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { fixed3 fogColor = _FogColor.rgb; //官方的宏定义:正向渲染时的额外通道 //不太好理解,做个小改变将fogColor改成红色(1,0,0),外面的fog颜色改成白色,在试试效果,就会明白了 #ifdef UNITY_PASS_FORWARDADD fogColor = (1,0,0); #endif //lerp函数的作用,即是在 第一个元素 与 第二个元素 之间利用 第三个元素 进行线性插值 //举例来说,当 IN.fog = 0 时,插值结果为color.rgb ,IN.fog = 1 时, 插值结果为fogColor。 //这样我们就可以通过设定IN.fog的值来控制雾化的百分比 //详细点,当IN.fog更接近0的时候,整个模型会显示大部分color.rgb,和小部分fogColor,反之同理 color.rgb = lerp (color.rgb, fogColor, IN.fog); } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
step6
线性雾 (Linear Fog)
Shader"Example / AutisticPatient SurfaceShader13" { Properties { _MainTex("MainTex",2D) = "white"{} } SubShader { Tags { "RenderType"="Opaque 9d56 " } //细节层次设为:200 //它是Level of Detail的缩写,在这里例子里我们指定了其为200 LOD 200 CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor vertex:myvert sampler2D _MainTex; //变量名顾名思义 //uniform是常量,也就是在shader代码中不可被改变的量 uniform half4 unity_FogColor; uniform half4 unity_FogStart; uniform half4 unity_FogEnd; struct Input { float2 uv_MainTex; half fog; }; void myvert (inout appdata_full v, out Input data) { //主要是将叫[data]的变量清空改成Input类型。 //initialize的中文含义便是初始化,试着理解 UNITY_INITIALIZE_OUTPUT(Input,data); //mul,矩阵相乘运算函数,不需要开发者自己手动去进行矩阵相乘 //mul计算结束后得到的是一个float4,然后取出其xyz //length函数用于取一个向量的长度,如果是float3则采取如下形式: //float length(float3 v) //{ // return sqrt(dot(v,v)); //} float pos = length(mul (UNITY_MATRIX_MV, v.vertex).xyz); float diff = unity_FogEnd.x - unity_FogStart.x; float invDiff = 1.0f / diff; //clamp(x,a,b) 如果x 值小于 a,则返回a;如果 x 值大于 b,返回b;否则,返回 x data.fog = clamp ((unity_FogEnd.x - pos) * invDiff, 0.0, 1.0); } //跟上一篇一样,不赘述了 void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { fixed3 fogColor = unity_FogColor.rgb; //正向渲染时的额外通道 #ifdef UNITY_PASS_FORWARDADD fogColor = 0; #endif color.rgb = lerp (fogColor, color.rgb, IN.fog); } void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } } }
从这几篇开始,我已经开始慢慢的放弃一些代码的注释了,原因呢(实在是直白的就略过了)
本系列结束了,都是些简单到不能再简单的东西,感觉就像是复习了一遍3D数学基础,和API
接下来会进行官网的其他系列翻译,我在发表之前会吸收很多前辈的经验,当然肯定有理解的不够深刻的地方,欢迎指正,共同学习。
所有的我参考过的博客都会放第二个文章里
相关文章推荐
- UnityShader官方案例讲解——SurfaceShader(2)
- UnityShader官方案例讲解——SurfaceShader(3)
- UnityShader官方案例讲解——SurfaceShader(1)
- UnityShader官方案例之表面着色器光照示例
- UnityShader官方案例之编写顶点和片段着色器
- unity, 在surface shader中访问顶点色
- unity 3D c# 协程的典型用法-Unity官方案例精讲笔记
- Unity Shader——Writing Surface Shaders(1)——Surface Shader Examples
- Unity Shader——Writing Surface Shaders
- UnityShader案例(二)——Phong高光反射
- unity, multi pass shader中的surface pass
- Unity Shader——Writing Surface Shaders(0)
- Unity&Shader案例篇—屏幕渐暗效果
- UnityShader案例(三)——BlinnPhong高光反射
- Unity Shaders and Effects Cookbook (7-2) Surface Shader 中实现 顶点动画
- 【Unity】Surface Shader的Input输入结构
- 菜鸟学习 - Unity中的热更新 - uLua官方 案例
- Unity SurfaceShader 开始编程
- unity surface shader植物