您的位置:首页 > 移动开发 > Unity3D

Unity Shader入门精要笔记(十四):渐变纹理

2017-09-03 22:46 746 查看
本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢。 http://blog.csdn.net/lzhq1982/article/details/77659332
从上一篇我们看到纹理不只是为了定义颜色,还可以存储很多表面属性,这一篇说的渐变纹理就是其中一种,比如用渐变纹理控制漫反射效果。前面光照篇里我们介绍过漫反射,是用法线和光照方向的点积与材质的反射率相乘得到。这里我们可以用渐变纹理更加灵活的控制光照。
下面是本篇的效果图:



可以看出,用不同的渐变纹理可以自由控制物体漫反射效果,左边是用一张从紫色到浅黄色的渐变纹理的效果;中间比较通用,是和《军团要塞2》类似,从黑色向浅灰色渐变,中间分界线微微发红。右侧常用于卡通风格的渲染,这种色调通常是突变的,模拟卡通的阴影色块。
先上代码:

Shader "CustomShader/Texture/RampShader"
{
Properties
{
_RampTex ("Texture", 2D) = "white" {}
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
Pass
{
Tags {"LightMode" = "ForwardBase"}

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "Lighting.cginc"

sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _Color;
fixed4 _Specular;
float _Gloss;

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
float3 worldNormal : TEXCOORD2;
};

v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _RampTex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = UnityObjectToWorldNormal(v.normal);

return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));

fixed halfLambert = dot(worldNormal, lightDir) * 0.5 + 0.5;
fixed3 color = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

fixed3 diffuse = _LightColor0.rgb * color;

fixed3 halfDir = normalize(viewDir + lightDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}

FallBack "Specular"
}
下面我们来分析下上面的代码,虽然有些长,但其实很简单,我们发现剥离出光照部分,余下的并不多。

先看Porperties部分,重点是这行:
_RampTex ("Texture", 2D) = "white" {}

这就是我们的渐变纹理。然后我们定义了对应的变量:
sampler2D _RampTex;

float4 _RampTex_ST;

_RampTex_ST是纹理名加_ST,用于在顶点着色器中计算纹理坐标用的,也就是这句:
o.uv = TRANSFORM_TEX(v.uv, _RampTex);

具体原理看基础单张纹理这篇吧。
顶点着色器没做什么,除了上面说的计算渐变纹理的纹理坐标,就是把顶点的坐标和法线转换到世界空间并传给片元着色器做光照处理。UnityObjectToClipPos(v.vertex)这句是unity5.6以上支持的,对应以前的版本mul(UNITY_MATRIX_MVP,
v.vertex)。
片元着色器中重点的是这两句:
fixed halfLambert = dot(worldNormal, lightDir) * 0.5 + 0.5;

fixed3 color = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;

如果还记得基础漫反射这篇里的半兰伯特模型的话,就会知道第一句是用半兰伯特模型计算出漫反射系数,其值是在[0,
1],忘了的童鞋就去回顾一下吧,这里就不介绍了。我们知道如果按正常的光照漫反射,应该是下面这个样子:
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;

这里我们要用纹理代替这个计算,该怎么办呢,那就是第二句,用第一句求出来的漫反射系数,作为渐变纹理的纹理坐标来采样。我们可以想一想,虽然我们的渐变纹理是二维的,但其颜色是一维的,只有从左到右的颜色变化,我们可以称之为一维纹理,这样 fixed2(halfLambert, halfLambert)
这句其实我们只关心其x坐标,y坐标没意义。那么halfLambert为什么可以与纹理坐标联系起来呢,我们从漫反射的公式中看出,法线与光照方向的夹角决定了光照系数,夹角越小,光照系数越大,漫反射越强,反之,系数越小,漫反射越弱,那么我们用渐变纹理的x轴表示其系数变化,x轴也就是系数越小,就采样越黑的颜色,表示漫反射越弱,系数越大也就是x越大,就采样越白的颜色,表示漫反射越强,所以我们的渐变纹理是从左到右从黑到白,就是这个原理。
其他的部分都是光照的处理,这里就不介绍了。其实我们可以举一反三,这里只是用渐变纹理模拟漫反射效果,在实际的应用中,我们有很多地方可以用到渐变纹理,比如水面的深浅颜色过渡等,只要你可以用[0, 1]的数值变化表现纹理的变化,都可以模拟出你想要的效果,所以shader的世界很多时候是只有你想不到,没有你做不到。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息