Cg 编程/Unity/最小图像效果
本教程介绍了在 Unity 中创建最小图像效果以对摄像机视图进行图像后期处理的基本步骤。如果您不熟悉纹理处理,您应该先阅读 “纹理球体”一节。
虚拟摄像机渲染图像后,通常需要对图像应用一些图像后期处理。这样做有艺术上的原因(例如,实现特定的视觉风格),但也有技术上的原因(例如,在图像后期处理中实现动态环境光遮蔽或景深通常比将其作为渲染的一部分实现这些效果更有效)。
在 Unity 中,每个图像后期处理步骤称为“图像效果”。每个图像效果都包含一个 C# 脚本和一个着色器文件,该文件指定一个片段着色器,该着色器计算输出图像的像素。为了应用着色器,我们还需要创建一个材质,如下所述。
为图像效果创建 Cg 着色器并不复杂:在 **项目窗口** 中,单击 **创建** 并选择 **着色器 > 图像效果着色器**。项目窗口中应该出现一个名为“NewImageEffectShader”的新文件。双击它打开它(或右键单击并选择 **打开**)。应该会出现一个带有 Cg 中默认着色器的文本编辑器。
以下着色器比默认着色器更有用。您可以将其复制并粘贴到着色器文件中
Shader "tintImageEffectShader"
{
Properties
{
_MainTex ("Source", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
}
SubShader
{
Cull Off
ZWrite Off
ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vertexShader
#pragma fragment fragmentShader
#include "UnityCG.cginc"
struct vertexInput
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct vertexOutput
{
float2 texcoord : TEXCOORD0;
float4 position : SV_POSITION;
};
vertexOutput vertexShader(vertexInput i)
{
vertexOutput o;
o.position = mul(UNITY_MATRIX_MVP, i.vertex);
o.texcoord = i.texcoord;
return o;
}
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
float4 fragmentShader(vertexOutput i) : COLOR
{
float4 color = tex2D(_MainTex,
UnityStereoScreenSpaceUVAdjust(
i.texcoord, _MainTex_ST));
return color * _Color;
}
ENDCG
}
}
Fallback Off
}
该着色器具有两个属性:_Color
是一个颜色,该着色器使用它来对所有像素的颜色进行着色。_MainTex
是一个渲染纹理,其中包含由摄像机渲染的摄像机视图,或者它是先前图像效果的输出渲染纹理。渲染纹理对象可以像 2D 纹理一样用于纹理化,但摄像机也可以像渲染帧缓冲区一样渲染到其中。渲染纹理非常适合图像效果,因为摄像机(或先前的图像效果)可以将图像渲染到其中,然后可以将图像馈送到下一个图像效果,就像它是纹理一样。
该着色器停用面剔除(使用 Cull Off
)和深度测试(使用 ZTest Always
),以确保处理整个图像。它还停用写入深度缓冲区(使用 ZWrite Off
),以避免更改深度缓冲区。
顶点着色器 vertexShader()
将标准变换应用于顶点位置,并传递纹理坐标。对于图像效果,顶点通常是摄像机视口的角点。纹理坐标指定这些角点在纹理坐标空间中的位置(从 0 到 1)。
然后,对输出图像的每个像素调用片段着色器 fragmentShader()
。它可以使用插值的纹理坐标访问输入图像 _MainTex
中的像素。对于某些目标平台(尤其是虚拟现实中的立体渲染),需要对纹理坐标进行额外的变换;这些由 Unity 的 UnityStereoScreenSpaceUVAdjust()
函数处理。
此片段着色器只是读取输入图像中对应像素的颜色,并将其乘以 _Color
来对其进行着色。更高级的图像效果的片段着色器会读取输入图像中不同位置的多个像素的颜色,并将它们以复杂的方式组合起来计算每个输出像素的颜色。
为该着色器创建材质的一种方法是,在 **项目窗口** 中单击 **创建 > 材质**。然后,您可以将着色器拖放到新材质上。当您选择新材质时,**检查器窗口** 中的预览应该显示着色器的效果;如果它没有显示,您可能需要单击检查器窗口底部的灰色栏使其出现。如果没有预览或球体是亮粉红色,则应该在 Unity 窗口底部和 **控制台** 窗口中显示错误消息(您可以从主菜单使用 **窗口 > 常规 > 控制台** 打开它)。在这种情况下,您需要修复着色器文件中的错误。
最后一步是将材质及其着色器应用于摄像机视图的图像。这需要一个小的脚本,该脚本必须附加到摄像机;以下是在 C# 中的示例,应将其保存为“tintImageEffectScript.cs”
using System;
using UnityEngine;
[RequireComponent(typeof(Camera))]
[ExecuteInEditMode]
public class tintImageEffectScript : MonoBehaviour {
public Material material;
void Start()
{
if (null == material || null == material.shader ||
!material.shader.isSupported)
{
enabled = false;
return;
}
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Graphics.Blit(source, destination, material);
}
}
此脚本有一个公共变量 material
,必须将其设置为创建的材质。Start()
函数只是检查一切是否正常;如果没有,它会停用脚本,从而停用图像效果。实际工作是在 OnRenderImage()
函数中完成的。Unity 会为每个图像效果调用此函数,其中输入图像(作为渲染纹理)位于 source
变量中,并期望输出图像位于 destination
变量中(这也是一个渲染纹理)。将材质及其着色器应用于 source
中的输入图像的标准方法是调用 Graphics.Blit(source, destination, material)
,它使用 material
中的着色器,以 source
作为输入纹理 _MainTex
,对 destination
渲染纹理中的所有像素进行光栅化。
许多图像效果的脚本要复杂得多,因为它们计算多个中间图像(有时具有不同的尺寸),而不是仅仅使用一次调用 Graphics.Blit()
。在这些情况下,单个图像效果使用多个着色器。这通常使用具有多个通道的单个着色器文件来实现。然后,特定通道的着色器将与类似 Graphics.Blit(source, destination, material, pass)
的调用一起使用,其中 pass
是使用的通道的索引。
恭喜,您已经了解了 Unity 中图像效果的基础知识。您已经看到的一些内容是
- 如何为图像效果创建着色器。
- 如何为这样的着色器创建材质。
- 如何为图像效果创建 C# 脚本。
如果您还想了解更多信息
- 关于 Unity 中的标准图像效果,请参阅 Unity 的后期处理包 的描述。
- 关于将更强大的计算着色器用于图像效果,请参阅 “计算图像效果”一节。