本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。
这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。
========================================== 分割线 ==========================================
最近由于成为研一新生,被入学的各种事情耽误,好久没有更新博客,好惭愧。。。刚收拾好我就来更新博客啦~快表扬我一下。。。前几天有好心人给我提建议说代码和解释分开容易导致学习不连贯,其实我也有这个感觉。我我为什么不写到注释里呢?一个是因为解释的内容比较多,注释里面说不太清楚;更重要的原因是,我的Mono不支持中文输入。。。只能复制粘贴!如果有人知道Mac的Mono里面怎么输入中文可以告诉我。。。为了让学习过程更连贯,以后的“解释”部分大部分都会移到“实现”中每一步的解释里面!
这节是一个新的章节,倒数第二章喽~Render Texture是一个非常好用的东西,通过本章我们就来学习如何用它来实现一些画面特效,像老电影效果、雪花效果等等。
先来介绍一下本章。学习编写Shader一个很有用的地方就是可以创建各种自定义的画面特效,也被称为后期特效(post effects)。使用这些画面特效,我们可以创建很多美妙的实时图像,例如高光(Bloom),运动模糊(Motion Blur),HDR特效(HDR effects)等等。大多数市面上的现代游戏使用了很多这样的画面特效,以此来得到景深效果,高光效果,甚至是进行颜色矫正。
通过本章,我们将学习如何建立一个脚本系统,来控制创建这些画面特效。我们会学习什么是render texture,什么是深度缓冲,以及怎样像Photoshop那样创建画面特效。通过这些内容,你不仅可以进一步加深Shader知识,还可以自己动脑在Unity中创建很棒的实时渲染。
那么怎样创建画面特效呢?
一般,我们会抓取一个完整的画面图像(或纹理),使用Shader在GPU上处理它的像素后,再返回给Unity的渲染器渲染到屏幕上,也就是一个后期处理的过程。这允许我们可以对渲染后的游戏图像进行实时地逐像素操作,从而给了我们一个全局的控制能力。
我们想象一个场景。我们需要调整游戏最后的画面对比度。如果我们去调整每个材质,虽然的确可能做到,但这会非常麻烦。而通过利用画面特效,我们就可以整体调整画面最后的效果。
为了让我们的画面特效系统能够建立并正常运行,我们需要创建一个单独的脚本来作为游戏当前已渲染的图像(也就是Unity的render texture)的通信员。这个脚本会把当前的render texture传递给Shader。
我们第一个画面特效是一个非常简单的灰度效果。那,开始吧!
我们总共需要一个脚本和一个Shader。
脚本:负责在编辑中实时更新图像,同时也负责从主摄像机抓取render texture,然后把该texture传递给Shader。
Shader:一旦render texture到达Shader后,我们就在这里面进行逐像素操作。
首先,我们来实现C#脚本。
[ExecuteInEditMode]
public class TestRenderImage : MonoBehaviour {public class TestRenderImage : MonoBehaviour {
#region Variables
public Shader curShader;
public float grayScaleAmount = 1.0f;
private Material curMaterial;
#endregion #region Properties
public Material material {
get {
if (curMaterial == null) {
curMaterial = new Material(curShader);
curMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return curMaterial;
}
}
#endregion // Use this for initialization
void Start () {
if (SystemInfo.supportsImageEffects == false) {
enabled = false;
return;
}
if (curShader != null && curShader.isSupported == false) {
enabled = false;
}
} void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture){
if (curShader != null) {
material.SetFloat("_LuminosityAmount", grayScaleAmount);
Graphics.Blit(sourceTexture, destTexture, material);
} else {
Graphics.Blit(sourceTexture, destTexture);
}
} // Update is called once per frame
void Update () {
grayScaleAmount = Mathf.Clamp(grayScaleAmount, 0.0f, 1.0f);
} void OnDisable () {
if (curMaterial != null) {
DestroyImmediate(curMaterial);
}
}using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class TestRenderImage : MonoBehaviour {
#region Variables
public Shader curShader;
public float grayScaleAmount = 1.0f;
private Material curMaterial;
#endregion
#region Properties
public Material material {
get {
if (curMaterial == null) {
curMaterial = new Material(curShader);
curMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return curMaterial;
}
}
#endregion
// Use this for initialization
void Start () {
if (SystemInfo.supportsImageEffects == false) {
enabled = false;
return;
}
if (curShader != null && curShader.isSupported == false) {
enabled = false;
}
}
void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture){
if (curShader != null) {
material.SetFloat("_LuminosityAmount", grayScaleAmount);
Graphics.Blit(sourceTexture, destTexture, material);
} else {
Graphics.Blit(sourceTexture, destTexture);
}
}
// Update is called once per frame
void Update () {
grayScaleAmount = Mathf.Clamp(grayScaleAmount, 0.0f, 1.0f);
}
void OnDisable () {
if (curMaterial != null) {
DestroyImmediate(curMaterial);
}
}
}
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_LuminosityAmount ("GrayScale Amount", Range(0.0, 1.0)) = 1.0
} SubShader {
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"uniform sampler2D _MainTex; fixed _LuminosityAmount;
fixed4 frag(v2f_img i) : COLOR
{
//Get the colors from the RenderTexture and the uv‘s
//from the v2f_img struct
fixed4 renderTex = tex2D(_MainTex, i.uv);
//Apply the Luminosity values to our render texture
float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;
fixed4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);
return finalColor;
}Shader "Custom/ImageEffect" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_LuminosityAmount ("GrayScale Amount", Range(0.0, 1.0)) = 1.0
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
fixed _LuminosityAmount;
fixed4 frag(v2f_img i) : COLOR
{
//Get the colors from the RenderTexture and the uv‘s
//from the v2f_img struct
fixed4 renderTex = tex2D(_MainTex, i.uv);
//Apply the Luminosity values to our render texture
float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;
fixed4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);
return finalColor;
}
ENDCG
}
}
FallBack "Diffuse"
}
上面的过程展示了我们如何使用一种方便的方法检验画面特效Shader。下面,我们来更深入地了解这其中render texture到底发生了什么变化,以及从头到尾它们到底是怎么被处理的。
画面特效是顺序处理的,这就像Photoshop中的layers。如果你有多个屏幕特效,你可以按顺序添加给该Camera,那么它们就会按照这个顺序被处理。
上述的过程是被简化过的,但通过这些我们可以看出画面特效的核心是如何工作的。
上面实现的是一个非常简单的画面特效系统。我们下面来看一下使用Unity的渲染器还可以做哪些有用的事情。
呜,这篇比较复杂,剩下的允许我明天来补坑。。。
使用Unity Render Textures实现画面特效——建立画面特效脚本系统
原文:http://blog.csdn.net/candycat1992/article/details/39255575