这是对unity shader语法的快速入门、复习
主要参考链接:
- shader从入门到跑路
- Unity 入门精要
Lesson 1 渲染管线
此部分可以参考我之前的文章:百人计划学习 图形 1.1 渲染流水线
主要分清楚顶点着色器、片段着色器阶段的输入和输出,以及这两个阶段是做什么的
Lesson 2 Shader的基本结构
Shader "Custom/ShaderLearning"
{
Properties {
_MainTex("Main Tex",2D) = "white"
}
SubShader{
Tags{"LightMode" = "ForwardBase"}
Pass{
Blend SrcAlpha OneMinusSrcAlpha // Traditional transparency
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular; //因为Specular是0-1所以精度更高用fixed4
float _Gloss;
struct a2v{
float4 vertex : POSITION; 将模型的顶点坐标填充到vertex变量中
float2 uv : TEXCOORD0;
};
struct v2f{
float2 uv : TEXCOORD0; //
float4 pos : SV_POSITION; //pos包含了顶点在裁剪空间中的位置信息
};
sampler2D _MainTex;
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); //转换到屏幕空间
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) :SV_Target {
fixed4 color = tex2D(_MainTex,i.uv);
color *= fixed4(i.uv.x,i.uv.y,0,1);
return color;
}
ENDCG
}
}
FallBack "Diffuse"
}
解析一下上面的各部分:
properties属性部分
Properties {
_Tex("Tex", 2D) = "white" {}
_Intensity("Intensity", Range(0, 20)) = 2 //锐化强度
_MainTex("Main Tex",2D) = "white"
}
- Subshader,定义了一系列pass以及可选状态,pass是子着色器
- Fallback,告诉unity,如果上面所有的subshader在这块显卡上都不执行,那么使用这个最低级的shader
Lesson 3 纹理输入
将带有透明通道的PNG图片渲染出来
如果没有设置透明通道,会出现以下情况
注意:在属性区块内被定义的数据在CGPROGRAM内被使用之前都要预定义一遍,不然会报错。在CGPROGRAM内加入纹理的定义:
sampler2D _Tex;
float4 _Tex_ST;
float _Intensity;
要返回纹理,片段着色器应该写为:
fixed4 frag(v2f i) :SV_Target {
return tex2D(_MainTex,i.uv);
}
设置透明度,
使用Alpha Blend :
Blend SrcAlpha OneMinusSrcAlpha
OutColor = SrcColor * ScrAlpha + DstColor * (1 - SrcAlpha)
代码如下:
Shader "Custom/TextureShader"
{
Properties {
_MainTex ("Main Tex", 2D) = "white"
}
SubShader{
Tags{"LightMode" = "ForwardBase"}
Tags{"Queue" = "Transparent"}
Pass{
Blend SrcAlpha OneMinusSrcAlpha // Traditional transparency
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
#include "Lighting.cginc"
struct a2v{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) :SV_Target {
return tex2D(_MainTex,i.uv);
}
ENDCG
}
}
FallBack "Diffuse"
}
Lesson 4 屏幕后处理效果
在颜色缓冲区的内容真正被喷到屏幕上之前,我们仍然有机会更改其中的内容。而unity也为我们考虑到了这一点,因此它提供了一个叫OnRenderImage的函数,这是个monobehaviour事件。
Called after scene rendering is complete to allow post-processing
of the image,Blit 将 dest 设置为渲染目标,在材质上设置 source _MainTex 属性, 并绘制全屏四边形。所以在material上要设置 _MainTex
- 完整shader是lesson1中的代码
- 下面的脚本添加到camera中
using UnityEngine;
[ExecuteInEditMode]
public class PostEffects : MonoBehaviour
{
public Material material;
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Graphics.Blit(source,destination,material);
}
}
Lesson 5 扭曲效果制作
需要添加位移图、和扭曲强度参数
位移图红色作为uv的x值,所以取到红色就是向右移动
绿色同理,向上移动
这里实现的是动态扭曲的效果,有两点需要注意
一是_Time的使用,
二十disp*2 -1 将范围划到(-1,1)中uv值就随着时间的推移而改变,我们在对位移图进行采样的时候,uv值就已经改变过了
会重复的原因是tex2D的原因,最终会把纹理uv的值映射到0-1中(没有完全明白)
float4 frag(v2f i) :SV_Target {
float2 distuv = float2(i.uv.x + _Time.x ,i.uv.y + _Time.x*2);
float2 disp = tex2D(_DisplaceTex,distuv).xy;
disp = ((disp*2) - 1) * _Magnitude;
float4 color = tex2D(_MainTex,i.uv + disp);
return color;
}
Lesson 6 小练习
小练习1
实现两个图片的替换
添加两个参数:
- _SubTex (“Sub Tex”,2D) = “white” {}
- _Between (“Tween”,Range(0,1)) = 0
在frag shader里
fixed4 frag(v2f i) :SV_Target {
return tex2D(_MainTex,i.uv)*(1-_Between) + tex2D(_SubTex,i.uv)*(_Between);
}
2.设置灰度值并乘以自定义颜色
使用明亮度(luminance)公式来算出一个明亮度值,然后将红绿蓝通道都设置为明亮度值,透明度通道则用对图片采样获得的透明度,最终获得灰度效果
luminance = 0.2125 * Red + 0.07154 * Green + 0.0721 * Blue
fixed4 frag(v2f i) :SV_Target {
fixed4 colorVal = tex2D(_MainTex,i.uv)*(1-_Between) + tex2D(_SubTex,i.uv)*(_Between);
fixed luminance = 0.2125 * colorVal.r + 0.07154 * colorVal.g + 0.0721 * colorVal.b;
return fixed4(luminance,luminance,luminance,tex2D(_MainTex,i.uv).a);
}