Browse Source

开源笔记迁移

master
ZGC 3 years ago
commit
e914b91e34
  1. 566
      ImageEffectShader屏幕后期处理解析.md
  2. 1999
      UnityShader相关模型公式.md
  3. 532
      UnityShader编写指南.md
  4. BIN
      img/2021-12-29-14-45-35-image.png
  5. BIN
      img/ImageEffect/DepthTexture1.png
  6. BIN
      img/ImageEffect/DepthTexture2.png
  7. BIN
      img/ImageEffect/DepthTexture3.png
  8. BIN
      img/ImageEffect/DepthTexture4.png
  9. BIN
      img/ImageEffect/DepthTexture5.png
  10. BIN
      img/ImageEffect/DepthTexture6.png
  11. BIN
      img/ImageEffect/DepthTexture7.png
  12. BIN
      img/ImageEffect/DepthTexture8.gif
  13. BIN
      img/ImageEffect/DepthTexture9.gif
  14. BIN
      img/ImageEffect/net7.jpg
  15. BIN
      img/ToonWaterShader.gif
  16. BIN
      img/ToonWaterShader2.gif
  17. BIN
      img/UnpackNormal.png
  18. BIN
      img/abs_UnpackNormal.png
  19. BIN
      img/bumpTest4.png
  20. BIN
      img/bumpTest4.tif
  21. BIN
      img/bumpTest5.png
  22. BIN
      img/bumpTest5.tif
  23. BIN
      img/gradient.jpg
  24. BIN
      img/gradient2.jpg
  25. BIN
      img/normalMapPosition.png
  26. BIN
      img/texture.png
  27. 37
      法线贴图详解.md
  28. 195
      深度纹理进阶应用-卡通水体着色器制作.md

566
ImageEffectShader屏幕后期处理解析.md

@ -0,0 +1,566 @@ @@ -0,0 +1,566 @@
# 屏幕后期处理效果
在渲染完整个场景之后,得到的屏幕图像进行了一系列操作,最终实现各种屏幕特效,如Bloom,SSAO等等。
要实现屏幕后期处理的基础是,必须得到渲染后的屏幕图像,即抓取屏幕,而Unity为我们提供了这样一个方便的接口---OnRenderImage函数。
## 写在前面
简单介绍几个常用名词。
### 0.1 UV
U和V分别是图片在显示器水平、垂直方向上的坐标,取值范围是0~1。通常是从左往右,从下往上从0开始依次递增。
## 1.基础类
在进行屏幕后期处理之前,我们需要检查一系列条件是否满足,例如当前平台是否支持渲染纹理跟屏幕特效。是否支持当前使用的shader等。为此,我们创建了一个用于屏幕后期处理效果的基类。在实现各种屏幕特效时,我们只需继承基础类。在实现派生类中不同操作即可。
```csharp
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
[RequireComponent (typeof(Camera))]
public class PostEffectsBase : MonoBehaviour {
// 开始检查
protected void CheckResources() {
bool isSupported = CheckSupport();
if (isSupported == false) {
NotSupported();
}
}
// 在检查是否支持屏幕后期shader的时候调用
protected bool CheckSupport() {
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) {
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}
return true;
}
// 在不支持屏幕后期shader的时候调用。
protected void NotSupported() {
enabled = false;
}
protected void Start() {
CheckResources();
}
// 在需要通过该屏幕后期shader创建相应材质的时候调用。
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) {
if (shader == null) {
return null;
}
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported) {
return null;
}
else {
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
}
```
### 基础类使用案例(继承PostEffectBase)
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ImageEffectGeneral : PostEffectsBase{
public Shader scannerShader;
private Material scannerMaterial;
public Material material
{
get
{
// 检查是否生成对应材质
scannerMaterial = CheckShaderAndCreateMaterial(scannerShader, scannerMaterial);
return scannerMaterial;
}
}
//定义相关数据调整
public string dataTags = "";
public float dataScale = 1.0f;
public new void Start()
{
//如果需要提前规定Camera获取深度纹理或者法线纹理,需要提前声明
//GetComponent<Camera>().depthTextureMode = DepthTextureMode.DepthNormals;
}
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
//GetComponent<Camera>().depthTextureMode = DepthTextureMode.DepthNormals;
if (material != null)
{
//需要传入的数据,需要在Shader中定义好对应变量名称,作为传入依据。
material.SetFloat(dataTags, dataScale);
Graphics.Blit(src, dest, material);
}
else
{
Graphics.Blit(src, dest);
}
}
}
```
## 2.使用渲染纹理
在Unity中,获取深度纹理是非常简单的,直接在脚本中设置摄像机的depthTextureMode,就可以获取对应的纹理数据。
深度纹理获取
```csharp
GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth;
```
法线纹理+深度纹理获取:
```csharp
GetComponent<Camera>().depthTextureMode = DepthTextureMode.DepthNormals;
```
#### 深度纹理采样
当在Shader中访问到深度纹理 _CameraDepthTexture 后,我们就可以使用当前像素的纹理坐标对它进行采样。绝大多数情况下,我们直接使用tex2D函数采样即可,但在某些平台(例如PS3或者PSP)上,我们需要一些特殊处理。Unity为我们提供了一个统一的宏,SAMPLE_Depth_Texture,用来处理这些由于平台差异造成的问题。
#### 法线纹理采样
用以下语句进行的法线采样,是范围在-1~1之间的水平&垂直法线值,如果需要展示成法线贴图,需要手动对normal进行归一化处理 normal = 0.5*normal+0.5
```cpp
fixed3 normal = DecodeViewNormalStereo(tex2D(_CameraDepthNormalsTexture,i.uv));
```
#
# 屏幕后期处理效果实例
## 1.uv应用:扭曲
通过重新计算uv,采用黑白图来对uv进行重新赋值,可以产生扭曲的效果。
```cpp
Shader "Hidden/ImageDistort"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_DistortTex ("Distort Texture", 2D) = "white" {}
_DistortScale ("Distort Scale",Range(0,1)) = 1.0
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _DistortTex;
float _DistortScale;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//i.uv = abs(0.5-i.uv);
fixed4 noise = tex2D(_DistortTex,i.uv);
i.uv = i.uv + (0.5-noise.r) * _DistortScale*0.1;
fixed4 col = tex2D(_MainTex, i.uv);
// just invert the colors
//col.rgb = 1 - col.rgb;
return col;
}
ENDCG
}
}
}
```
使用时,可以参考上述中的,基础类使用案例,编写对应的cs脚本并挂在摄像机上,通过调整相关参数来观看对应效果
## 2.深度纹理应用:扫描效果探究
深度图的每一个像素值表示场景中某点与摄像机的距离。
是指将从图像采集器到场景中各点的距离(深度)作为像素值的图像,它直接反映了景物可见表面的几何形状。
shader中,深度图的获取可以通过以下代码进行
```cpp
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth = Linear01Depth(depth);
fixed4 col = fixed4(linearDepth,linearDepth,linearDepth,1);
```
当通过纹理采样SAMPLE_DEPTH_TEXTURE之后,得到的深度值往往是非线性的。然而,我们的计算过程中通常是需要线性的深度值,也就是说,我们需要把投影后的深度值变换到线性空间下。
最终结果如下,上图为原始场景,下图为场景中提取的深度图(通过Linear01Depth解码)
0表示该点和摄像机处于同一位置,1表示该点位于视锥体的远裁剪平面上(摄像机Near=0.3 Far=50)
![avatar](./img/ImageEffect/DepthTexture1.png)
LinearEyeDepth负责把深度纹理的采样结果转换到视角空间下的深度值
LinearEyeDepth解码,此时的摄像机远裁剪平面与近裁剪平面并不会对结果产生影响,通过调节参数_DepthParam,看对应的变化区间(下图为_DepthParam为10的样子,即黑的部分是离镜头10m以内的,白色部分是离镜头11m以外的,模糊的部分为距离10-11m之间的值)
```cpp
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth = LinearEyeDepth(depth);
float diff = linearDepth-_DepthParam;
```
![avatar](./img/ImageEffect/DepthTexture3.png)
使用floor函数,对上述的值进行取整,并对采样值归一化,可以得到比较尖锐化的边缘,此时深度在11m范围内的部分会全部变成黑色。
```cpp
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth = LinearEyeDepth(depth);
float diff = linearDepth-_DepthParam;
diff = saturate(floor(diff));
```
![avatar](./img/ImageEffect/DepthTexture4.png)
通过负数取值归一化,可对采样值进行反色,让黑的部分变白,白的部分变黑。
```cpp
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth = LinearEyeDepth(depth);
float diff = linearDepth-_DepthParam;
diff = saturate(-diff);
```
![avatar](./img/ImageEffect/DepthTexture5.png)
上述操作中,如果不进行归一化(取值范围限制在0-1之间)的话,黑的部分可能会产生负数,白的部分可能会超过1,最终可能计算不出正确结果。
将上述两张图按加算合起来,得到一个按深度扫描的效果雏形
```cpp
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth = LinearEyeDepth(depth);
float diff = linearDepth-_DepthParam;
diff = saturate(floor(diff))+saturate(-diff);
```
![avatar](./img/ImageEffect/DepthTexture6.png)
得到这部分后,因为归一化前面已经做过了,我们仅需要对上述结果进行反色处理(1-diff),再混合上原背景(对原图像进行插值处理,此时我们用到前面所提到的插值函数lerp),就可以看到对应的扫描效果了。
```cpp
fixed4 mainTex = tex2D(_MainTex, i.uv);
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth = LinearEyeDepth(depth.r);
fixed4 col = fixed4(linearDepth,linearDepth,linearDepth,1);
float diff = linearDepth-_DepthParam;
fixed4 border = 1-(saturate(floor(diff))+saturate(-diff*0.5));
fixed4 final = lerp(mainTex,border*_DepthColor,border);
return final;
```
![avatar](./img/ImageEffect/DepthTexture7.png)
下面提供完整代码
ExampleScannerImage.shader
```cpp
Shader "Hidden/ExampleScannerImage"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_DepthParam ("DepthParam",Float) = 1.0
_DepthColor("DepthColor",Color) = (1,1,1,1)
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _CameraDepthTexture;
float _DepthParam;
fixed4 _DepthColor;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 mainTex = tex2D(_MainTex, i.uv);
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth = LinearEyeDepth(depth.r);
float diff = linearDepth-_DepthParam;
fixed4 border = 1-(saturate(floor(diff))+saturate(-diff*0.5));
fixed4 final = lerp(mainTex,border*_DepthColor,border);
return final;
}
ENDCG
}
}
}
```
PostEffects.cs(参考本篇开头)
ImageScannerEffect.cs
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ImageScannerEffect : PostEffectsBase
{
public Shader scannerShader;
private Material scannerMaterial;
public Material material
{
get
{
// 检查是否生成对应材质
scannerMaterial = CheckShaderAndCreateMaterial(scannerShader, scannerMaterial);
return scannerMaterial;
}
}
//定义相关数据调整
public float dataScale = 1.0f;
public Color dataColor = Color.white;
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
//GetComponent<Camera>().depthTextureMode = DepthTextureMode.DepthNormals;
if (material != null)
{
material.SetFloat("_DepthParam", dataScale);
material.SetColor("_DepthColor", dataColor);
Graphics.Blit(src, dest, material);
}
else
{
Graphics.Blit(src, dest);
}
}
}
```
使用时,将ImageScannerEffect.cs拖拽至主摄像机中,并且把ExampleScannerImage.shader 这个文件拖至Scanner Shader选项卡中去。
录制动画,调节脚本中DataScale选项依次增大,完成相关扫描特效的制作。
可以根据自己喜好,调整相关颜色等操作。
添加图片遮罩,根据法线纹理生成网格纹理等等我们后续会接着讲。
![avatar](./img/ImageEffect/DepthTexture8.gif)
## 3.纹理附加:扫描效果探究(续)
在上图中,我们由于为对插值处理进行相关优化,所以在尾部会出现一些黑色的部分,这些黑色的部分是因为原本的图像是呈现黑色的。
这次我们使用一张附加纹理,使得扫描效果更具科技感一些
这次是我们使用的附加纹理原图
![avatar](./img/ImageEffect/net7.jpg)
在使用上述图片之前,我们需要了解uv转换函数TRANSFORM_TEX
TRANSFORM_TEX方法比较简单,就是将模型顶点的uv和Tiling、Offset两个变量进行运算,计算出实际显示用的定点uv。
如果对_MaskTex这个纹理进行uv转换,必须声明变量_MaskTex_ST(float4类型)
实际调整的时候,_MaskTex_ST.xy代表Tilling(缩放程度),_MaskTex_ST.zw代表Offset(偏移程度)
我们在声明变量的时候,沿用_MaskTex的同时下方添加一个ST的声明即可
```cpp
sampler2D _MaskTex;
float4 _MaskTex_ST;
float _MaskTexTillY;
```
片元着色器
```cpp
_MaskTex_ST.y = _MaskTexTillY;
i.uv = TRANSFORM_TEX(i.uv, _MaskTex);
fixed4 maskTex = tex2D(_MaskTex,i.uv);
```
在附加纹理时,可以通过两种方式进行,一种是附加法,一种是累乘法
我们这里使用了带颜色的附加法进行(因为包含了黑色,原本是黑色的部分就不会变化)
同样的,我们在原本的基础上使用了_DepthColor.a 作为底图的影响程度,这样可以淡化尾部的黑色部分。
```cpp
fixed4 final = lerp(mainTex,saturate(border*_DepthColor+mainTex*_DepthColor.a+maskTex*_DepthColor),saturate(border));
```
改写完shader后,在cs脚本中添加以下内容即可
```csharp
public Texture dataTex;
public float tillY;
```
OnRenderImage函数中
```csharp
material.SetTexture("_MaskTex", dataTex);
material.SetFloat("_MaskTexTillY", tillY);
```
shader完整代码
```c
Shader "Hidden/ExampleScannerImage"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_MaskTex("Mask Texture", 2D) = "white" {}
_MaskTexTillY("Till Y",Float) = 1.0
_DepthParam ("DepthParam",Float) = 1.0
_DepthColor("DepthColor",Color) = (1,1,1,1)
_DepthLength ("DepthLength",Float) = 1.0
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _CameraDepthTexture;
sampler2D _MaskTex;
float4 _MaskTex_ST;
float _MaskTexTillY;
float _DepthParam;
fixed4 _DepthColor;
float _DepthLength;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 mainTex = tex2D(_MainTex, i.uv);
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth = LinearEyeDepth(depth.r);
float diff = linearDepth-_DepthParam;
fixed4 border = 1-(saturate(floor(diff))+saturate(-diff*_DepthLength));
_MaskTex_ST.y = _MaskTexTillY;
i.uv = TRANSFORM_TEX(i.uv, _MaskTex);
fixed4 maskTex = tex2D(_MaskTex,i.uv);
fixed4 final = lerp(mainTex,saturate(border*_DepthColor+mainTex*_DepthColor.a+maskTex*_DepthColor),saturate(border));
return final;
}
ENDCG
}
}
}
```
具体效果截图
![avatar](./img/ImageEffect/DepthTexture9.gif)
此时,我们的扫描效果就已经比上一个阶段高出一个档次了

1999
UnityShader相关模型公式.md

File diff suppressed because it is too large Load Diff

532
UnityShader编写指南.md

@ -0,0 +1,532 @@ @@ -0,0 +1,532 @@
# 前言
本编程指南仅尽可能的列出编写一个shader的参考
#
# shader编写结构
## 1.定义Properties(方便在创建材质的时候可以传入贴图等数据)
## 2.编写SubShader主题
## 2.1【Tags】
## 2.2【RenderSetUp】
### 2.2.1【LOD】渲染深度(详情参考Unity LOD 远景渲染部分)
## 2.3【GrabPass】【UsePass】【Pass】【Pass】(第二个或者其他)
----------------------案例Pass--------------------
【RenderSetUp】
CGPROGRAM
//编译顶点着色器
#pragma vertex vert
//编译片元着色器
#pragma fragment frag
### 2.3.1 其他引用以及其它编译指令
### 2.3.2 声明参数
### 2.3.3 设置应用数据->顶点着色器结构体a2v,appdata(a2v为app to vert简写)
### 2.3.4 设置顶点->片源着色器结构体v2f(v2f为vert to fragment简写)
### 2.3.5 编写顶点着色器渲染方法
v2f vert (a2v v){
v2f o;
//着色器内容
return o;
}
### 2.3.5 编写片源着色器渲染方法
fixed4 frag (v2f i) : SV_Target{
fixed4 tex = ...;
//着色器内容
return tex;
}
ENDCG
---------------------------------------------------
## 3.设置Fallback,如果显卡无法运行上面的SubShader,则执行FallBack预设进行渲染
# 1.Properties(参考书籍P30)
--------------------------------------------------------------------------
### 传入数据常用类型
## Int
_Int("Int",Int)=2
## Float
_Float("Float",Float)=1.0
## Range
_Range("Range",Range(0.0,5.0))=1.0
## Color
_Color("Color",Color)=(1,1,1,1)
## Vector
_Vector("Vector",Vector)=(1,1,1,1)
## 2D
_MainTex ("Texture", 2D) = "white" {}
ps:内置纹理名称包含white、black、gray、bump等等
## Cube
_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}
## 3D
_3D ("3D", 3D) = "black" {}
--------------------------------------------------------------------------
# 2.Tags(参考网站:https://gameinstitute.qq.com/community/detail/121997)
--------------------------------------------------------------------------
### 渲染类型声明(包含渲染顺序)
Tags的书写方法为键值对,可多可少,不需要全部声明,未声明时按默认值
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
# 2.1(SubShader)Tags
## key:Queue
渲染顺序,括号内为优先度,值越小越先被渲染
## value
Background(1000)
Geometry(2000)【默认】
AlphaTest(2450)
Transparent(3000)
Overlay(4000)
可以自定义值例如"Queue"="3100"
## key:RenderType
渲染类型
## value
Opaque-----------------不透明(法线、自发光、反射、地形Shader)
Transparent------------半透明(透明、粒子、字体、地形添加通道Shader)
TransparentCutout------遮罩透明(透明裁切、双通道植物Shader)
Background-------------天空盒Shader
Overlay----------------GUI纹理、光晕、闪光Shader
TreeOpaque-------------地形引擎——树皮
TreeTransparentCutout--地形引擎——树叶
TreeBillboard----------地形引擎——公告牌(始终面向摄像机)式树木
Grass------------------地形引擎——草
GrassBillboard---------地形引擎——公告牌(始终面向摄像机)式草
## key:DisableBatching
是否禁用Batch(打包、合并)
## value
True-------------------禁用
False------------------不禁用(默认)
LODFading--------------当LOD fade开启的时候禁用,一般用在树木上面
## key:ForceNoShadowCasting
是否强制不投射阴影,当这个值为True的时候,使用这个Shader的对象便不会投射阴影。
## key:IgnoreProjector
无视投影器,当这个值为True的时候,对象便不受投射器影响。
## key:CanUseSpriteAtlas
可使用精灵集,当这个值为False的时候,不能使用精灵集。
## key:PreviewType
材质的预览形式,默认显示为球体,可以使用Plane(2D平面)或Skybox(天空盒)
# 2.2(Pass)Tags
## key:LightMode
光照模式
## value
Always------------------总是渲染,不使用光照
ForwardBase-------------用于前向渲染,使用环境光、主平行光、顶点/SH(球谐函数)光照以及光照贴图
ForwardAdd--------------用于前向渲染,额外使用每像素光,每个光照一个通道
Deferred----------------用于延迟着色,渲染G-Buffer
ShadowCaster------------渲染对象的深度到阴影贴图或者深度纹理
PrepassBase-------------用于(旧版)延迟光照,渲染法线和高光指数
PrepassFinal------------用于(旧版)延迟光照,合并贴图、光照和自发光来渲染最终色彩
Vertex------------------当对象不受光照贴图影响的时候,用来渲染(旧版)顶点发光。使用所有的顶点光照
VertexLMRGBM------------当对象接受光照贴图影响的时候,用来渲染(旧版)顶点发光。适用于使用RGBM编码光照贴图的平台(PC&主机)
VertexLM----------------当对象接受光照贴图影响的时候,用来渲染(旧版)顶点发光。适用于使用double-LDR编码光照贴图的平台(移动平台)
## key:PassFlags
标志渲染管线如何传递数据给通道
## value
OnlyDirectional---------只有主平行光、环境光和光照探测器的数据会传递给通道。仅用于LightMode为ForwardBase
## key:RequireOptions
标志通道至于在某些外部条件满足时才会被渲染
SoftVegetation----------当Quality Setting中的Soft Vegetation选项被开启时,才会渲染通道
# 3.RenderSetUp(以下写在CGPROGRAM之前)(参考书籍P31)
--------------------------------------------------------------------------
## 3.1 Cull
Cull Back | Front | Off
设置剔除模式:剔除背面/正面/关闭剔除
## 3.2 ZTest
ZTest Less Greater | LEqual | GEqual | Equal | NotEqual | Always
设置深度测试时使用的函数
## 3.3 ZWrite
ZWrite On | Off
开启/关闭深度写入
## 3.4 Blend
BlendOp (Op)
设置混合操作
Blend (SrcFactor) (DstFactor)
Blend (SrcFactor) (DstFactor),(SrcFactorA) (DstFactorA)
开启并设置混合模式(透明材质用)
例如:Blend SrcAlpha OneMinusSrcAlpha
其中SrcFactor为源颜色乘以的混合因子
DstFactor为目标颜色乘以的混合因子
两者相加存入颜色缓冲区
Blend混合操作表(参考书籍P174)
-----------------------------------------
Add | 源颜色和目标颜色相加
Sub | 源颜色减去目标颜色
RevSub | 目标颜色减去源颜色
Min | 使用目标颜色跟源颜色中较小的值
Max | 使用源颜色跟目标颜色中较大的值
Blend混合因子表(参考书籍P174)
-----------------------------------------
One | 1
Zero | 0
SrcColor | 源颜色值
SrcAlpha | 源颜色的透明度值
DstColor | 目标颜色值
DstAlpha | 目标颜色的透明度值
OneMinusSrcColor | 1-源颜色值
OneMinusSrcAlpha | 1-源颜色的透明值
OneMinusDstColor | 1-目标颜色值
OneMinusDstAlpha | 1-目标颜色的透明度值
常用混合类型
-----------------------------------------
正常--------| Blend SrcAlpha OneMinusSrcAlpha
柔向相加----| Blend OneMinusDstColor One
正片叠底----| Blend DstColor Zero
两倍相乘----| Blend DstColor SrcColor
变暗--------| BlendOp Min
------------| Blend One One
变亮--------| BlendOp Max
------------| Blend One One
滤色--------| Blend OneMinusDstColor One
线性减淡----| Blend One One
# 4.Pass
--------------------------------------------------------------------------
渲染所使用的通道,可以使用抓取屏幕图像的Pass--GrabPass
以及引用其他shader的Pass--UsePass
以及自己编写的Pass
# 5.vert & frag
--------------------------------------------------------------------------
顶点着色器,编写案例如下
需要前置结构体a2v
输出结构体v2f
(应用->顶点坐标->顶点着色器->输出顶点)
v2f vert (a2v v){
v2f o;
//着色器内容
return o;
}
需要前置结构体v2f
输出着色器颜色值fixed4
(顶点->片元着色器->输出颜色)
fixed4 frag (v2f i) : SV_Target{
fixed4 tex = ...;
//着色器内容
return tex;
}
## 5.0 结构体语义(参考书籍P110)
### a2v------从应用阶段传递模型数据给顶点着色器
| 语义 | 描述 | 类型 |
| ----------- | ------------------------------------------ | ------------- |
| POSITION | 模型空间的顶点位置 | float4 |
| NORMAL | 顶点法线 | float3 |
| TANGENT | 顶点切线 | float3 |
| TEXCOORD0-7 | 该顶点的纹理坐标,可以定义TEXCOORD0,TEXCOORD1表示第一组或者第二组 | float2&float4 |
| COLOR | 顶点颜色 | fixed4&float4 |
### v2f------从顶点着色器传递数据给片元着色器
| 语义 | 描述 | 类型 |
| ----------- | ----------------- | ------ |
| SV_POSITION | 裁剪空间中的顶点坐标 | float4 |
| COLOR0 | 通常用于输出第一组顶点颜色,非必需 | fixed4 |
| COLOR1 | 通常用于输出第二组顶点颜色,非必需 | fixed4 |
| TEXCOORD0-7 | 用于输出纹理坐标0-7 | 任意 |
### SV_Target------片元着色器输出时用的语义
输出值将会存储到渲染目标(Render Target)中。
### VPOS/WPOS------片元着色器传递屏幕坐标的语义
类型为float4,xy为屏幕空间中的坐标,z为近裁剪平面处于远裁剪平面处的分量,范围0-1。对于w,如果摄像机投影类型为正交投影,w恒为1,如果使用透视投影,w的取值范围为[1/Near,1/Far],Near为近裁剪平面处于摄像机的距离,Far为远裁剪平面处于摄像机的距离。
## 5.1 变换矩阵(参考书籍P87)
-----------------------------------------
### UNITY_MATRIX_MVP(取模现在被UnityObjectToClipPos(v.vertex)所替代)
当前模型的观察投影矩阵,顶点/方向矢量 模型空间->裁剪空间
### UNITY_MATRIX_MV
当前的模型观察矩阵,顶点/方向矢量 模型空间->观察空间
### UNITY_MATRIX_V
当前的观察矩阵,顶点/方向矢量 世界空间->观察空间
### UNITY_MATRIX_P
当前的投影矩阵,顶点/方向矢量 观察空间->裁剪空间
### UNITY_MATRIX_VP
当前的观察投影矩阵,顶点/方向矢量 世界空间->裁剪空间
### UNITY_MATRIX_T_MV
UNITY_MATRIX_MV的转置矩阵
### UNITY_MATRIX_IT_MV
UNITY_MATRIX_MV的逆转置矩阵,用于将发现从模型空间变换到观察空间,也可用于得到
UNITY_MATRIX_MV的逆矩阵
### unity_ObjectToWorld(原为_Object2World)
当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间
### unity_WorldToObject(原为_World2Object)
unity_ObjectToWorld的逆矩阵,用于将顶点/方向矢量从世界空间变换到模型空间
## 5.2 Unity内置的摄像机和屏幕参数(参考书籍P88)
-------------------------------------------------
### 【float3】 _WorldSpaceCameraPos
该摄像机在世界空间中的位置
### 【float4】 _ProjectionParams
x = 1.0 & -1.0 | y = Near | z = Far | w = 1.0 + 1.0 / Far
Near = 近裁剪平面到摄像机的距离
Far = 远裁剪平面到摄像机的距离
### 【float4】 _ScreenParams
x = width | y = height | z = 1.0 + 1.0 / width | w = 1.0 + 1.0 / height
width = 该摄像机的渲染目标的像素宽度
height = 该摄像机的渲染目标的像素高度
### 【float4】 _ZBufferParams
x = 1 - Far / Near | y = Far / Near | z = x / Far | w = y / Far
该变量用于线性化Z缓存中的深度值
### 【float4】unity_OrthoParams
x = width | y = height | z = 无定义 | w = 1.0(该摄像机为正交摄像机) & 0.0(该摄像机为透视摄像机)
width = 正交投影摄像机的宽度
height = 正交投影摄像机的高度
### 【float4x4】unity_CameraProjection
该摄像机的投影矩阵
### 【float4x4】unity_CameraInvProjection
该摄像机的投影矩阵的逆矩阵
### 【float4[6]】unity_CameraWorldClipPlanes[6]
该摄像机的6个裁剪平面在世界空间下的等式
按如下顺序:左、右、上、下、近、远裁剪平面
## 5.3 UnityCG.cginc常用的帮助函数(参考书籍P137)
-------------------------------------------------
### 【float3】 WorldSpaceViewDir(float4 v)
输入:模型空间中的顶点位置
返回:世界空间红从该点到摄像机的观察方向。
ps:内部实现使用了UnityWorldSpaceViewDir函数
### 【float3】 UnityWorldSpaceViewDir(float4 v)
输入:世界空间中的顶点位置
返回:世界空间中从该点到摄像机的观察方向。
### 【float3】 ObjectSpaceViewDir(float4 v)
输入:一个模型空间中的顶点位置
返回:模型空间中该点到摄像机的观察方向。
### 【float3】 WorldSpaceLightDir(float4 v)【仅用于前向渲染】【未被归一化】
输入:一个模型空间中的顶点位置
返回:世界空间中从该点到光源的光照方向。
ps:内部实现使用了UnityWorldSpaceLightDir函数
### 【float3】 UnityWorldSpaceLightDir(float4 v)【仅用于前向渲染】【未被归一化】
输入:世界空间中的顶点位置
返回:世界空间中该点到光源的光照方向
### 【float3】 ObjSpaceLightDir(float4 v)【仅用于前向渲染】【未被归一化】
输入:一个模型空间中的顶点位置
返回:模型空间中从该点到光源的光照方向
### 【float3】 UnityObjectToWorldNormal(float3 normal)
把法线方向从模型空间转换到世界空间中
### 【float3】 UnityObjectToWorldDir(float3 dir)
把方向矢量从模型空间变换到世界空间中
### 【float3】 UnityWorldToObjectDir(float3 dir)
把方向矢量从世界空间变换到模型空间中
# 6.关于光照
## 6.1 Unity渲染路径
------------------------------------------------------------
在tag-LightMode里设置,详情见本文2.2
## 6.2 Unity内置光照变量和函数
------------------------------------------------------------
### 【float4】 _LightColor0
该Pass处理的逐像素光源的颜色
### 【float4】 _WorldSpaceLightPos0
该Pass处理的逐像素光源的位置。
如果该光源是平行光,_WorldSpaceLightPos0.w=0
如果是其他类型光,_WorldSpaceLightPos0.w=1
### 【float4x4】 _LightMatrix0
从世界空间到光源空间的变换矩阵。可以用于采样cookie和光照衰减纹理
### 【float4】 unity_4LightPosX0 unity_4LightPosY0 unity_4LightPosZ0
仅用于BasePass
前四个非重要的点光源在世界空间中的位置
### 【float4】 unity_4LightPosX0 unity_4LightPosY0 unity_4LightPosZ0
仅用于BasePass
前四个非重要的点光源在世界空间中的位置
### 【float4】 unity_4LightAtten0
仅用于BasePass
前四个非重要的点光源的衰减因子
### 【half4[4]】 unity_LightColor
仅用于BasePass
前四个非重要的点光源的颜色
## 6.3 Unity前向渲染可以使用的内置光照参数
------------------------------------------------------------
以下函数仅用于前向渲染才可使用。
### 【float3】 WorldSpaceLightDir(float4 v)【未被归一化】
输入:一个模型空间中的顶点位置
返回:世界空间中从该点到光源的光照方向。
ps:内部实现使用了UnityWorldSpaceLightDir函数
### 【float3】 UnityWorldSpaceLightDir(float4 v)【未被归一化】
输入:一个世界空间中的顶点位置
返回:世界空间中从该点到光源的光照方向。
### 【float3】 ObjSpaceLightDir(float4 v)【未被归一化】
输入:一个模型空间中的顶点位置
返回:模型空间中从该点到光源的光照方向。
### 【float3】 Shade4PointLights(...)
输入:已经打包进矢量的光照数据,参考6.2
返回:计算逐顶点光照。

BIN
img/2021-12-29-14-45-35-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
img/ImageEffect/DepthTexture1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 KiB

BIN
img/ImageEffect/DepthTexture2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
img/ImageEffect/DepthTexture3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

BIN
img/ImageEffect/DepthTexture4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
img/ImageEffect/DepthTexture5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
img/ImageEffect/DepthTexture6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
img/ImageEffect/DepthTexture7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

BIN
img/ImageEffect/DepthTexture8.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 KiB

BIN
img/ImageEffect/DepthTexture9.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
img/ImageEffect/net7.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
img/ToonWaterShader.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
img/ToonWaterShader2.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
img/UnpackNormal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
img/abs_UnpackNormal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
img/bumpTest4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
img/bumpTest4.tif

Binary file not shown.

BIN
img/bumpTest5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
img/bumpTest5.tif

Binary file not shown.

BIN
img/gradient.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
img/gradient2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
img/normalMapPosition.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
img/texture.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

37
法线贴图详解.md

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
# 法线贴图
法线贴图是一种用于表现平面倾斜度的数据。
法线的取值范围是一个半球,如下图所示,处于半球边缘的任意点都可以代表法线的朝向。
法线贴图实际是将每个点的法线方向,按照颜色rgba的方式存储。其中r代表x轴旋转度,g代表y轴旋转度,b实际是轴本身的旋转,几乎无意义所以基本恒为1。
由于颜色值rgba属性中,不能为负值,取值范围为0-1之间的四组数值。所以将0.5作为垂直表面的法线基准值。故无凹凸表现的平面颜色值为(0.5,0.5,1,1);即为图中蓝点。
![avatar](./img/normalMapPosition.png)
图1 将坐标转换成颜色的图解
由于法线贴图不包含海拔信息,故向右凸起等于向左凹陷,反之向左凸起等于向右凹陷。比如下方图片旋转180度,感受一下是不是就像是凹陷了一样。
![avatar](./img/bumpTest4.png)
图2 实际的法线贴图(包含一个凸出的八边形,如果旋转180度,则是凹陷的八边形)
# UnpackNormal函数应用
上一节讲了,由于法线贴图不能包含负值,所以在UnityShader的使用过程中,需要对法线进行一次变换,使之范围从原来的【 0 ~ 1 】转换为【 -1 ~ 1 】
故而 UnpackNormal ( packednormal ) = packednormal.xyz * 2 – 1
图2 执行完该方法后,会变成图3所示的样子
![avatar](./img/UnpackNormal.png)
图3 UpackNormal 方法执行后
到这里可能会有人奇怪了,为什么左方跟下方都是一样的颜色。这个方法执行之后,实际上黑的部分是负数,负数的颜色是没法正确表现的,故变成了黑色。我们进行一次abs绝对值函数处理,就能看到对应颜色啦。
![avatar](./img/abs_UnpackNormal.png)
图4 abs绝对值处理

195
深度纹理进阶应用-卡通水体着色器制作.md

@ -0,0 +1,195 @@ @@ -0,0 +1,195 @@
# 卡通水体的制作
话不多说,先上效果图
![avatar](./img/ToonWaterShader2.gif)
该水体编写时,用到了GrabPass抓取屏幕,深度纹理,法线变换,菲涅尔反射,噪声纹理,以及顶点动画相关的知识
本篇中,仅解释编写原理,源码会在最后方放出,后续会不定期更新文档。
## 水体部分
##### 1.透明物体的制作
众所周知,水是大自然中透明的物体之一,首先我们需要做一个透明的物体。
这时,我们可以用到GrabPass抓取屏幕与透明度混合两种方式编写。
但由于透明度混合无法对后面的物体产生更改,故无法很好的模拟折射效果。
GrabPass抓取到图像后,经过变换获取到的视口空间中的坐标,从而模拟透明图像,这种方式做出来的透明图像,可以进行更多的操作,比如uv变换等等,可以很方便的模拟折射效果。
##### 2.使用法线贴图
##### 3.模拟菲涅尔反射
##### 4.模拟折射
## 浪花部分
##### 1.深度纹理描边
##### 2.泛光特效
##### 3.使用噪声纹理模拟浪花
##
## 通过顶点动画让水体动起来
##### 1.sin函数顶点变换
##### 2.时间参数获取
##
## 全部代码
```c
//卡通水体
//深度纹理与菲涅尔反射的综合应用
Shader "Unlit/ToonWaterShader"
{
Properties
{
// 水体颜色
_WaterColor ("WaterColor", COLOR) = (1,1,1,1)
// 浪花深度
_DepthScale("Depth Scale",Float) = 0.1
// 波浪噪声纹理
_WaveNoise("WaveTexture",2D) = "white" {}
// 菲涅尔反射颜色
_ReflectColor ("Reflect Color",Color) = (0, 0, 1, 1)
// 浅水区颜色
_DepthColor ("Depth Color",Color) = (0, 0, 1, 1)
// 菲涅尔反射幅度
_FresnelScale ("Fresnel Scale", Range(0, 1)) = 1
// 水体法线
_BumpMap ("Normal Map", 2D) = "bump" {}
// 法线影响度
_BumpScale("Bump Scale" ,Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
LOD 100
//抓取屏幕图像,存在名为_RefractionTex的变量中
GrabPass { "_RefractionTex" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// 声明抓取的变量
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
// 声明深度图
sampler2D _CameraDepthTexture;
//声明Properties各项变量
sampler2D _WaveNoise;
float4 _WaveNoise_ST;
float _DepthScale;
fixed4 _WaterColor;
fixed4 _ReflectColor;
fixed _FresnelScale;
fixed4 _DepthColor;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
//定义顶点着色器结构体
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 tangent : TANGENT;
float3 normal : NORMAL;
};
//定义片元着色器结构体
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 scrPos : TEXCOORD1;
float3 worldPos : TEXCOORD2;
fixed3 worldNormal : TEXCOORD3;
fixed3 worldViewDir : TEXCOORD4;
//TBN矩阵
half3 wNormal : TEXCOORD5;
half3 wTangent : TEXCOORD6;
half3 wBitangent : TEXCOORD7;
};
v2f vert (appdata v)
{
v2f o;
//定义偏移参数,根据时间变换顶点,使x轴顶点按照sin曲线运动
float4 offset;
offset.xyzw = float4(0.0,0.0, 0.0, 0.0);
//offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
offset.x = cos(_Time.y + v.vertex.z)*0.3;
o.vertex = UnityObjectToClipPos(v.vertex+offset);
//
o.uv = v.uv;
o.scrPos = ComputeGrabScreenPos(o.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
//计算TBN矩阵所用的三组变量。
o.wTangent = UnityObjectToWorldDir(v.tangent.xyz);
o.wNormal = UnityObjectToWorldNormal(v.normal);
// compute bitangent from cross product of normal and tangent
// 通过计算法线与切线的叉积,得到二次切线bitangent,叉积*切线方向
// half tangentSign = v.tangent.w * unity_WorldTransformParams.w;
// output the tangent space matrix
half tangentSign = v.tangent.w;
o.wBitangent = cross(o.wNormal, o.wTangent) * tangentSign;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldViewDir = normalize(i.worldViewDir);
half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv));
float3x3 TBNMatrix = float3x3(i.wTangent,i.wBitangent,i.wNormal);
worldNormal = mul(tnormal,TBNMatrix);
//half3 worldViewDir = UnityWorldSpaceViewDir(i.worldPos);
half3 worldRefra = refract(worldViewDir,worldNormal,_BumpScale);
float2 offset = worldRefra.xy;
//i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
// sample the texture
//fixed4 refrCol = tex2D(_RefractionTex,i.uv);
fixed4 refrCol = tex2D(_CameraDepthTexture, i.scrPos.xy/i.scrPos.w);
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
fixed4 refrColScreen = tex2D(_RefractionTex,i.scrPos.xy/i.scrPos.w);
fixed4 WaveNoise = tex2D(_WaveNoise,i.uv);
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
float linearDepth = LinearEyeDepth(refrCol.r);
float diff = linearDepth - i.scrPos.w;
fixed4 intersect = fixed4(1,1,1,1)-fixed4(diff*_DepthScale,diff*_DepthScale,diff*_DepthScale,1);
fixed4 border = floor(saturate(intersect+WaveNoise)*2);
fixed4 depthColor = (1-saturate(diff)) * _DepthColor;
fixed4 reflection = _ReflectColor;
fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5)*_BumpScale;
fixed4 diffuse = _WaterColor*refrColScreen+depthColor+border;
fixed4 colorfinal = lerp(diffuse, reflection, saturate(fresnel));
return colorfinal;
}
ENDCG
}
}
}
```
Loading…
Cancel
Save