文章目录
- 自言自语
- 效果
- shader代码
- 总结
自言自语
最近学习非真实感渲染的UnityShader相关知识,根据书本上的内容 自己按照自己的需求做了个效果 先贴上代码。 后边空了整理注释一下 贴上效果 这次当草稿存档 稍后更新
牛年第一更
好吧 本来说稍后更新,结果一个年过的自己胖了10斤不说,还把学习彻底给停了,放飞自我了。今天总算是强迫自己把年前的坑重新梳理了一遍并做了修改调整
想做出来一个 风格线条作为风格的效果。算不上是素描了。就感觉像用圆珠笔一顿乱涂出来的效果。当然线条的样式和走向密度 颜色 都是可控的。 默认基本上是贴图上什么颜色 相应区域的位置 就是什么颜色的线条。 个人感觉还有点意思。 算是对书上这节内容的举一反三??
自言自语结束。更新效果和修改过的添加注释的代码。看看后边有没有时间做个包装过后的效果
效果
shader代码
Shader "TNShaderPractise/ShaderPraictise_Sketch"
{
Properties
{
//一堆属性
[HDR]_Color ("Color", Color) = (1,1,1,1)
[HDR]_CharaDark("素描颜色",Color)=(1,1,1,1)
[HDR]_Specolor("高光颜色",Color)=(1,1,1,1)
_pow("光滑度",Range(0.001,200))=1
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_NormalMap("法线贴图",2D)="bump"{}
_NormalScale("法线强度",Range(-2,2))=1
_shadowArea("暗部范围",Range(0,1))=0
_Tiles ("素描纹理密度", float) = 8
_OutlineWidth("描边宽度",Range(0,0.2))=0.0125
_OutlineColor("描边颜色",COLOR)=(0,0,0,0)
_Sketch01("素描纹理01",2D)="white"{}
_Sketch02("素描纹理02",2D)="white"{}
_Sketch03("素描纹理03",2D)="white"{}
_Sketch04("素描纹理04",2D)="white"{}
_Sketch05("素描纹理05",2D)="white"{}
_Sketch06("素描纹理06",2D)="white"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
//描边pass
NAME "OUTLINE"
//正面剔除
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD1;
};
fixed4 _Color ;
sampler2D _MainTex ;
float _OutlineWidth;
fixed4 _OutlineColor;
v2f vert (appdata_base v)
{
v2f o;
//转到视图空间下计算精准一些 书上这么说 暂且先这么记
float4 viewPos = float4(UnityObjectToViewPos(v.vertex),0);
float3 viewNormal = UnityObjectToViewPos(v.normal);
//Z方向上锁定为常量
viewNormal.z = -0.5;
viewNormal = normalize(viewNormal);
//这里float4分量最后得是1 书上写0 是不能描边的 个人理解是因为涉及到平移变换 顶点平移变换w分量得为1 否则不代表平移
viewPos= viewPos + float4(viewNormal,1)*_OutlineWidth;
//再变换回裁剪空间
o.pos = mul(UNITY_MATRIX_P,viewPos);
o.uv = v.texcoord;
return o;
}
//为描边颜色声明一个饱和度算法
half4 luminance (fixed4 col,float sat)
{
half lum = col.r*0.202+col.g*0.7125+col.b*0.0712;
fixed4 lumcol = fixed4 (lum,lum,lum,col.a);
fixed4 finalcol = lerp (lumcol,col,sat);
return finalcol;
}
fixed4 frag (v2f i):SV_Target
{
//描边颜色和贴图颜色正片叠底,目前常规做法
fixed4 col = tex2D (_MainTex,i.uv)*_Color;
//颜色相乘会变灰 设置一个4饱和度参数值 增加描边颜色饱和度
fixed4 finalCol = luminance(_OutlineColor*col,4);
//返回描边颜色
return finalCol;
}
ENDCG
}
Pass
{
Tags{"LightMode"="ForwardBase"}
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "AutoLight.cginc"
#include "UnityCG.cginc"
#include "Lighting.cginc"
//加阴影
#pragma multi_compile_fwdbase
struct a2v
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 worldPos : TEXCOORD1;
float4 uv : TEXCOORD2;
//声明两个权重向量作为存储器
float3 sketchWeight1 : TEXCOORD3;
float3 sketchWeight2 : TEXCOORD4;
float3 worldNormal : TEXCOORD6;
float4 TtoW1 : TEXCOORD7;
float4 TtoW2 : TEXCOORD8;
float4 TtoW3 : TEXCOORD9;
//加阴影 存索引5
SHADOW_COORDS (5)
};
fixed4 _Color;
fixed4 _CharaDark;
fixed4 _Specolor;
float _pow;
float _shadowArea;
sampler2D _MainTex ;
sampler2D _NormalMap;
float _NormalScale;
sampler2D _Sketch01;
sampler2D _Sketch02;
sampler2D _Sketch03;
sampler2D _Sketch04;
sampler2D _Sketch05;
sampler2D _Sketch06;
sampler2D _LightModel;
float _Tiles;
v2f vert (a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv =v.texcoord;
o.uv.xy = v.texcoord.xy;
//把ZW分量拿出来进行风格线纹理的tilling值 不会影响主贴图tilling
o.uv.zw = v.texcoord.xy*_Tiles;
//全部转换到世界空间下进行计算 方便加入法线贴图 虽然实例中模型为手绘贴图模型因此没有法线贴图 就不演示了
o.worldNormal = UnityObjectToWorldNormal(v.vertex);
o.worldPos = mul (unity_ObjectToWorld,v.vertex);
//把灯光方向移到片元着色器中去计算吧
//float3 lightDir = normalize(UnityWorldSpaceLightDir(o.worldPos)).xyz;
//初始化两个存储器各个分量的值为0
o.sketchWeight1 = float3 (0,0,0);
o.sketchWeight2 = float3 (0,0,0);
//TBN矩阵转换 常规写
float4 worldTangent = mul(unity_ObjectToWorld,v.tangent);
float3 worldBinormal = cross(o.worldNormal,worldTangent)*v.tangent.w;
o.TtoW1 = float4 (worldTangent.x,worldBinormal.x,o.worldNormal.x,1);
o.TtoW2 = float4 (worldTangent.y,worldBinormal.y,o.worldNormal.y,1);
o.TtoW3 = float4 (worldTangent.z,worldBinormal.z,o.worldNormal.z,1);
//阴影投影三剑客 定点着色器中收尾写
TRANSFER_SHADOW (o);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//这里去除模型顶点法线信息,改用法线贴图方式
//float3 normal = normalize (i.worldNormal);
//常规获取光源方向和视角方向
float3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)).xyz;
float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)).xyz;
//bllingPhong模型正常的半程向量计算
float3 vl = normalize(lightDir+viewDir);
//法线贴图TBN矩阵转换常规计算
half3 bump = UnpackNormal( tex2D(_NormalMap,i.uv.xy)).xyz;
bump.xy *= _NormalScale;
bump.z = sqrt (1-saturate(dot(bump.xy,bump.xy)));
float3 normal = float3 (dot(i.TtoW1.xyz,bump),dot(i.TtoW2.xyz,bump),dot(i.TtoW3.xyz,bump));
//常规bllingphong光照模型光照辐射度计算
half NdotL = saturate(dot(normal,lightDir));
half NdotV = saturate(dot(normal,vl));
//将光照辐射度扩大7倍范围 进行相应的判断使用
float sketchScale = NdotL*7.0;
//如果大于6 则说明基本不会有阴影
if(sketchScale >6.0)
{
//则什么都不做,就正常显示贴图本身
}
//剩下的就是根据阴影的一个范围 来计算该使用哪种密度的风格线纹理贴图 也就是算出其所占权重
else if (sketchScale>5.0)
{
i.sketchWeight1.x = sketchScale - 5.0;
}
else if (sketchScale>4.0)
{
i.sketchWeight1.x = sketchScale - 4.0;
i.sketchWeight1.y = 1 - i.sketchWeight1.x;
}
else if (sketchScale>3.0)
{
i.sketchWeight1.y = sketchScale -3.0;
i.sketchWeight1.z = 1 - i.sketchWeight1.y;
}
else if (sketchScale >2.0)
{
i.sketchWeight1.z = sketchScale - 2.0;
i.sketchWeight2.x = 1 - i.sketchWeight1.z;
}
else if (sketchScale > 1.0)
{
i.sketchWeight2.x = sketchScale - 1.0;
i.sketchWeight2.y = 1 - i.sketchWeight2.y;
}
else
{
i.sketchWeight2.y = sketchScale;
i.sketchWeight2.z = 1- i.sketchWeight2.y;
}
//用计算好的权重进行计算 在相应位置配置合适的纹理作为阴影风格化
fixed4 sketch1 = tex2D (_Sketch01,i.uv.zw)*i.sketchWeight1.x;
fixed4 sketch2 = tex2D (_Sketch02,i.uv.zw)*i.sketchWeight1.y;
fixed4 sketch3 = tex2D (_Sketch03,i.uv.zw)*i.sketchWeight1.z;
fixed4 sketch4 = tex2D (_Sketch04,i.uv.zw)*i.sketchWeight2.x;
fixed4 sketch5 = tex2D (_Sketch05,i.uv.zw)*i.sketchWeight2.y;
fixed4 sketch6 = tex2D (_Sketch06,i.uv.zw)*i.sketchWeight2.x;
//纯阴影下也就是背光下也要有风格线效果,则采用正常UV采样风格线贴图
fixed4 sketch7 = tex2D (_Sketch06,i.uv.zw)*_shadowArea;
//算出非阴影部分的权重值
float whiteWeight =saturate( 1 - i.sketchWeight1.x-i.sketchWeight1.y-i.sketchWeight1.z-i.sketchWeight2.x-i.sketchWeight2.y-i.sketchWeight2.z);
//正常采样主贴图纹理颜色
fixed4 mainTex =tex2D (_MainTex , i.uv.xy);
//用非阴影部分的权重值得出 我们希望他是白色的部分
float4 whiteCol = fixed4 (1,1,1,1)*whiteWeight;
//环境光..
fixed3 amb = UNITY_LIGHTMODEL_AMBIENT.xyz*mainTex;
//阴影投影片元着色器部分的ATTEN 标准写法 固定变量名
UNITY_LIGHT_ATTENUATION (atten , i , i.worldPos);
//这里算出最终只有风格线和白色留白部分用于后边插值计算的参考值, 并乘以一个风格线面积密度权重值 来控制是否以风格线形式显示还是以主贴图形式显示
fixed4 finalWhite = saturate(sketch1+sketch2+sketch3+sketch4+sketch5+sketch6+whiteCol)*_shadowArea;
//半兰伯特一下吧 并加入阴影投影参数 固定变量名 UNITY的宏里写好的
fixed diff = (NdotL*0.5+0.5)*atten;
//这一步是用插值来计算风格线所出现的区域并控制他的颜色为主贴图颜色和风格线颜色. 把原本贴图模拟成线条上色的感觉
fixed4 lineArea =saturate( lerp(1,_CharaDark*mainTex+diff*mainTex*_CharaDark*4,saturate(1-sketch7)));
//这里控制留白部分的颜色,和风格线阴影与留白的插值比重
mainTex =lerp (_Color,lineArea,1-finalWhite);
//高光
fixed4 specCol = _LightColor0*_Specolor*pow(NdotV,_pow)*atten;
//最终颜色
fixed4 finalCol = _LightColor0*_Color*mainTex+specCol;
//对还要加上环境光
finalCol.rgb+= amb;
return finalCol;
}
ENDCG
}
}
//FallBack一个有投影pass的基础shader
FallBack "Legacy Shaders/Diffuse"
}
总结
越学习 越觉得难 但坚持到现在,发现自己想要的一些简单的效果都可以通过努力实现了。 虽然这并不能运用于项目。但总归是有点进步。我这人没啥优点,就是会自我鼓励,自己给自己鼓劲儿 哈哈。 继续加油吧