文章目录
- 先尝试GL类来制作
- Shader
- CSharp
- 画个三角型
- 画个全屏的Quad
- 发现GL没有RenderTarget之类的
- 使用CommandBuffer来绘制全屏的Quad
- GL渲染到目标
- 另外优化
- Project
- References
先尝试GL类来制作
Shader
// jave.lin 2020.04.12 - 绘制一个全屏的Quad
Shader "Custom/DrawFullScreenQuad" {
SubShader {
Pass {
ZTest Always ZWrite Off Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
fixed4 color : COLOR;
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert (appdata v) {
v2f o;
o.vertex = v.vertex;
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target { return i.color; }
ENDCG
}
}
}
CSharp
画个三角型
GL.PushMatrix();
mat.SetPass(0);
GL.LoadOrtho();
GL.Begin(GL.TRIANGLE_STRIP);
// 注意顶点顺序,组合为:顺时针即可,Unity 逆时针为正面
GL.Color(new Color(1, 0, 0, 1));//red
GL.Vertex3(0, 0, 1);
GL.Color(new Color(0, 1, 0, 1));//green
GL.Vertex3(1, 0, 1);
GL.Color(new Color(0, 0, 1, 1));//blue
GL.Vertex3(0.5f, 0.5f, 1);
GL.End();
GL.Flush();
运行效果
借用我之前写的一篇:
里面有个图片要注意的是,Unity的三角面正面判定
画个全屏的Quad
private void DrawFullScreenQuadTesting()
{
//var camWPos = cam.transform.position;
mat.SetPass(0);
GL.Begin(GL.TRIANGLE_STRIP);
// 注意顶点顺序,组合为:顺时针即可,Unity 逆时针为正面
GL.Color(new Color(1, 0, 0, 1));//red
GL.Vertex3(-1, -1, 0);
GL.Color(new Color(0, 1, 0, 1));//green
GL.Vertex3(1, -1, 0);
GL.Color(new Color(0, 0, 1, 1));//blue
GL.Vertex3(-1, 1, 0);
GL.Color(new Color(1, 1, 0, 1));//yellow
GL.Vertex3(1, 1, 0);
GL.End();
}
运行效果:
发现GL没有RenderTarget之类的
全屏的效果算是弄出来的,但是向要GL调用底层绘制的内容想指定一下渲染目标,结果发现没有。。。哈哈,白忙活了。。。连RenderTarget都没有。 这么说Unity GL貌似没啥用了,除了可以话一些Line
来调试用。 看来要实现RT功能只能CommandBuffer
或是Camera
来设置RenderTarget
并绘制了。
使用姿势不对,其实可以使用RenderTexture.active来设置当前渲染目标
使用CommandBuffer来绘制全屏的Quad
为何需要全屏Quad并Render到RT的功能,因为后面制作SSSM(Screen Space Shadow Map)做准备的。
CommandBuffer 相关的内容,我写过4篇,可以查看我References的。
CSharp
using UnityEngine;
using UnityEngine.Rendering;
// jave.lin 2020.04.13 - 绘制全屏Quad
public class CmdBuffDrawFullScreen : MonoBehaviour
{
public Camera cam;
public Material mat;
[SerializeField] private RenderTexture sssmRT;
private Mesh mesh;
private CommandBuffer cmdBuff;
private void Start()
{
cam.depthTextureMode |= DepthTextureMode.Depth;
mesh = new Mesh();
mesh.vertices = new Vector3[]
{
// 这里的第三个z分量最好不要在这里设置,最好在shader中判断是GL还是DX平台来设置为近截面的z值就好
new Vector3(-1,1,0), // bl
new Vector3(-1,-1,0), // tl
new Vector3(1,-1,0), // tr
new Vector3(1,1,0), // br
};
mesh.uv = new Vector2[]
{
new Vector2(0,0), // bl
new Vector2(0,1), // tl
new Vector2(1,1), // tr
new Vector2(1,0), // br
};
mesh.triangles = new int[]
{
0, 1, 2,
0, 2, 3
};
}
private void OnPreRender()
{
if (sssmRT == null || sssmRT.width != Screen.width || sssmRT.height != Screen.height)
{
if (sssmRT) RenderTexture.ReleaseTemporary(sssmRT);
sssmRT = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);//, RenderTextureFormat.R8);
sssmRT.name = "sssmRT";
if (cmdBuff == null)
{
cmdBuff = new CommandBuffer();
cmdBuff.name = "After Depth TEX";
}
cmdBuff.Clear();
cmdBuff.SetRenderTarget(sssmRT);
cmdBuff.ClearRenderTarget(true, true, Color.white);
cmdBuff.DrawMesh(mesh, Matrix4x4.identity, mat);
cam.RemoveCommandBuffer(CameraEvent.AfterDepthTexture, cmdBuff);
cam.AddCommandBuffer(CameraEvent.AfterDepthTexture, cmdBuff);
}
}
private void OnDestroy()
{
if (sssmRT) RenderTexture.ReleaseTemporary(sssmRT);
if (cam != null && cmdBuff != null)
{
cam.RemoveCommandBuffer(CameraEvent.AfterDepthTexture, cmdBuff);
cmdBuff.Release();
}
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Graphics.Blit(sssmRT, (RenderTexture)null);
}
}
其中需要注意的地方
mesh.vertices = new Vector3[]
{
// 这里的第三个z分量最好不要在这里设置,最好在shader中判断是GL还是DX平台来设置为近截面的z值就好
new Vector3(-1,1,0), // bl
new Vector3(-1,-1,0), // tl
new Vector3(1,-1,0), // tr
new Vector3(1,1,0), // br
};
留意注意:这里的第三个z分量最好不要在这里设置,最好在shader中判断是GL还是DX平台来设置为近截面的z值就好
Shader
// jave.lin
Shader "Custom/CmdBuffDrawFullScreenQuadMat" {
SubShader {
ZTest Always ZWrite Off Cull Off
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _CameraDepthTexture;
v2f vert (appdata v) {
v2f o;
// jave.lin : 在这里我们处理GL与DX的差异
// GL的Z:-1~1,DX的Z:0~1
#if defined (SHADER_TARGET_GLSL)
v.vertex.z = -1;
#else
v.vertex.z = 0;
#endif
o.vertex = v.vertex;
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target {
float depth = tex2D(_CameraDepthTexture, i.uv).r;
#if defined (UNITY_REVERSED_Z)
depth = 1 - depth;
#endif
return depth;
}
ENDCG
}
}
}
shader的vert
函数中注意这么一段:
// CSharp层脚本的第三个z分量最好不要在这里设置
// 最好在shader中判断是GL还是DX平台来设置为近截面的z值就好
// jave.lin : 在这里我们处理GL与DX的差异
// GL的Z:-1~1,DX的Z:0~1
#if defined (SHADER_TARGET_GLSL)
v.vertex.z = -1;
#else
v.vertex.z = 0;
#endif
运行效果
GL渲染到目标
CSharp,主要调用的是:RenderTexture.active = RT;
using UnityEngine;
// jave.lin 2020.04.22 - 绘制一个全屏的Quad
public class DrawFullScreenQuadGL_Correct : MonoBehaviour
{
private static int _TestFullscreenTex_hash = Shader.PropertyToID("_TestFullscreenTex");
public enum TransformType
{
None, // 无,就是VS不处理任何变化,a2v的坐标当做clip space的坐标用
Generic // 一般化
}
public Camera cam;
public TransformType transType;
public Material noneTransformMat;
public Material genericTransformMat;
public bool drawMergeColor = true;
public Material depthMergeFullscreenColorMat;
private RenderTexture rt;
private void Start()
{
cam.depthTextureMode |= DepthTextureMode.DepthNormals;
}
private void OnRenderObject()
{
if (rt == null || rt.width != Screen.width || rt.height != Screen.height)
{
if (rt != null) RenderTexture.ReleaseTemporary(rt);
rt = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);
}
RenderTexture src = RenderTexture.active;
if (drawMergeColor)
{
RenderTexture.active = rt;
}
if (transType == TransformType.None) NoneTransformDraw();
else GenericTransformDraw();
if (drawMergeColor)
{
RenderTexture.active = src;
}
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (drawMergeColor)
{
depthMergeFullscreenColorMat.SetTexture(_TestFullscreenTex_hash, rt);
Graphics.Blit(source, destination, depthMergeFullscreenColorMat);
}
else
{
Graphics.Blit(source, destination);
}
}
private void OnDestroy()
{
if (rt != null) {
RenderTexture.ReleaseTemporary(rt);
rt = null;
}
}
private void NoneTransformDraw()
{
noneTransformMat.SetPass(0);
GL.Begin(GL.TRIANGLE_STRIP);
// 注意顶点顺序,组合为:顺时针即可,Unity 逆时针为正面
GL.Color(new Color(1, 0, 0, 1));//red
GL.Vertex3(-1, -1, 0);
GL.Color(new Color(0, 1, 0, 1));//green
GL.Vertex3(1, -1, 0);
GL.Color(new Color(0, 0, 1, 1));//blue
GL.Vertex3(-1, 1, 0);
GL.Color(new Color(1, 1, 0, 1));//yellow
GL.Vertex3(1, 1, 0);
GL.End();
}
private void GenericTransformDraw()
{
genericTransformMat.SetPass(0);
GL.PushMatrix();
GL.LoadOrtho();
GL.Begin(GL.QUADS);
// 注意顶点顺序,组合为:顺时针即可,Unity 逆时针为正面
GL.Color(new Color(1, 0, 0, 1));//red
GL.Vertex3(0, 0, 0); // bl
GL.Color(new Color(0, 1, 0, 1));//green
GL.Vertex3(0, 1, 0); // tl
GL.Color(new Color(0, 0, 1, 1));//blue
GL.Vertex3(1, 1, 0); // tr
GL.Color(new Color(1, 1, 0, 1));//yellow
GL.Vertex3(1, 0, 0); // br
GL.End();
GL.PopMatrix();
}
}
另外优化
后期渲染我们一般的会使用4个顶点,两个三角形来绘制全屏图像。
但后来发现这部分竟然还可以优化,说是因为tile-based渲染相关。
所以可以使用一个三角形来替代一个Quad(两个三角形)的方式来优化。
引用一张图:
在 Unity Shader URP 中,可以看到:Common.hlsl
中的 GetFullScreenTriangleTexCoord
和 GetFullScreenTriangleVertexPosition
也有类似的优化处理
// Generates a triangle in homogeneous clip space, s.t.
// v0 = (-1, -1, 1), v1 = (3, -1, 1), v2 = (-1, 3, 1).
float2 GetFullScreenTriangleTexCoord(uint vertexID)
{
#if UNITY_UV_STARTS_AT_TOP
return float2((vertexID << 1) & 2, 1.0 - (vertexID & 2));
#else
return float2((vertexID << 1) & 2, vertexID & 2);
#endif
}
float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE)
{
float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
return float4(uv * 2.0 - 1.0, z, 1.0);
}