本篇根据上、中篇的内容,进行总结,给出关键类的结构。
EffectRTGenerator
EffectRTGenerator是用来做渲染到纹理的辅助类。
- 通过Inst方法来访问EffectRTGenerator,Inst内部会在首次调用时创建一个实例,不需要在Scene预先放一个RTCamera碍眼,让开发人员完全不用去理会rt生成的细节
- CreateOutterline方法会创建一个轮廓的rendertexture。CreateOutterline的逻辑框架是:
1、_Begin(),生成rt,移动target
2、RenderWithShader
3、_End(),还原target
4、根据需求处理纹理 - 类的定位:EffectRTGenerator放在场景中“非常远”的位置,专门用来做渲染到纹理,其他对象有rt的相关需求时,只需要调用相关的方法即可生成rt。这个类的定位不仅仅是做轮廓rt的生成,而是扩展并支持各种类似rt的生成。例如,可以加入生成“发光”、“烟雾“等贴图,只要按CreateOutterline的模式进行改造就行了。
**注意:**CreateOutterline不能在target的Awake和Start里调用。
代码:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Camera))]
public class EffectRTGenerator : MonoBehaviour
{
//public int RTGenLayer = 31;
static EffectRTGenerator m_Instance;
public static EffectRTGenerator Inst()
{
if (m_Instance == null)
{
GameObject rtCamera = new GameObject("RTCamera");
rtCamera.transform.position = new Vector3(9999999, 9999999, -10);
Camera c = rtCamera.AddComponent<Camera>();
c.clearFlags = CameraClearFlags.Color;
c.backgroundColor = Color.black;
c.orthographic = true;
c.orthographicSize = 5;
c.nearClipPlane = 0.3f;
c.farClipPlane = 1000f;
c.enabled = false;
m_Instance = rtCamera.AddComponent<EffectRTGenerator>();
}
return m_Instance;
}
Material m_SolidMat = null;
Material m_BlurMaterial = null;
Material m_CutoffMaterial = null;
Material m_CompositeMaterial = null;
Vector3 m_TargetOldPosition;
Vector3 m_TargetOldEulerAngles;
Vector3 m_TargetOldScale;
int m_TargetOldLayer;
public void Awake()
{
m_Instance = this;
m_SolidMat = new Material(Shader.Find("Outterline/Solid"));
m_SolidMat.hideFlags = HideFlags.HideAndDontSave;
//m_SolidMat.shader.hideFlags = HideFlags.None;
m_BlurMaterial = new Material(Shader.Find("Outterline/ShapeBlur"));
m_BlurMaterial.hideFlags = HideFlags.HideAndDontSave;
//m_BlurMaterial.shader.hideFlags = HideFlags.None;
m_CutoffMaterial = new Material(Shader.Find("Outterline/CutoffBody"));
m_CutoffMaterial.hideFlags = HideFlags.HideAndDontSave;
//m_CutoffMaterial.shader.hideFlags = HideFlags.None;
m_CompositeMaterial = new Material(Shader.Find("Outterline/Composer"));
m_CompositeMaterial.hideFlags = HideFlags.HideAndDontSave;
//m_CompositeMaterial.shader.hideFlags = HideFlags.None;
// 设置到一个极其远的地方,以便于只渲染目标
transform.position = new Vector3(99999, 99999, -10);
//GetComponent<Camera>().cullingMask = 1 >> RTGenLayer;
}
void OnDestroy()
{
DestroyImmediate(m_SolidMat);
DestroyImmediate(m_BlurMaterial);
DestroyImmediate(m_CutoffMaterial);
DestroyImmediate(m_CompositeMaterial);
}
public RenderTexture _Begin(GameObject target, float x, float y)
{
// 根据输入的x和y,创建一个rt
RenderTexture rt = new RenderTexture((int)x, (int)y,0);
rt.useMipMap = true;
rt.filterMode = FilterMode.Bilinear;
rt.depth = 0;
rt.generateMips = true;
rt.Create();
GetComponent<Camera>().targetTexture = rt;
// 调整cemara的范围以适应rt
float cameraSize = y / 2;
GetComponent<Camera>().orthographicSize = cameraSize;
//float r = (float)Screen.width / Screen.height;
//float r_x = x / ( y * 1);
//GetComponent<Camera>().orthographicSize = cameraSize;
//GetComponent<Camera>().rect = new Rect(0, 0, r_x, 1);
// 保持旧的位置
m_TargetOldPosition = target.transform.position;
m_TargetOldEulerAngles = target.transform.eulerAngles;
m_TargetOldScale = target.transform.localScale;
//m_TargetOldLayer = target.layer;
// 移动目标到摄像机处,保证只有目标在摄像机的范围内
// 由于此用一个极远的位置,此处只有目标,所有可以不用设置layer
Vector3 pos = transform.position;
pos.z = 0;
target.transform.localScale = Vector3.one;
target.transform.position = pos;
target.transform.eulerAngles = Vector3.zero;
//target.layer = RTGenLayer;
return rt;
}
void _End(GameObject target)
{
// 还原
target.transform.position = m_TargetOldPosition;
target.transform.eulerAngles = m_TargetOldEulerAngles;
target.transform.localScale = m_TargetOldScale;
//target.layer = m_TargetOldLayer;
}
public RenderTexture CreateOutterline(GameObject target, float x, float y)
{
int iterations = 5;
float spread = 0.5f;
RenderTexture rt = _Begin(target, x, y);
GetComponent<Camera>().RenderWithShader(m_SolidMat.shader, "");
_End(target);
iterations = Mathf.Clamp(iterations, 0, 15);
spread = Mathf.Clamp(spread, 0.5f, 6.0f);
RenderTexture buffer = RenderTexture.GetTemporary(rt.width, rt.height, 0);
RenderTexture buffer2 = RenderTexture.GetTemporary(rt.width, rt.height, 0);
//buffer2.filterMode = FilterMode.Bilinear;
//buffer.filterMode = FilterMode.Bilinear;
//rt.filterMode = FilterMode.Bilinear;
Graphics.Blit(rt, buffer);
bool oddEven = true;
for (int i = 0; i < iterations; i++)
{
if (oddEven)
_FourTapCone(buffer, buffer2, i, spread);
else
_FourTapCone(buffer2, buffer, i, spread);
oddEven = !oddEven;
}
if (oddEven)
{
Graphics.Blit(rt, buffer, m_CutoffMaterial);
Graphics.Blit(buffer, rt, m_CompositeMaterial);
}
else
{
Graphics.Blit(rt, buffer2, m_CutoffMaterial);
Graphics.Blit(buffer2, rt, m_CompositeMaterial);
}
RenderTexture.ReleaseTemporary(buffer);
RenderTexture.ReleaseTemporary(buffer2);
return rt;
}
void _FourTapCone(RenderTexture source, RenderTexture dest, int iteration, float spread)
{
float off = 0.5f + iteration * spread;
Graphics.BlitMultiTap(source, dest, m_BlurMaterial,
new Vector2(off, off),
new Vector2(-off, off),
new Vector2(off, -off),
new Vector2(-off, -off)
);
}
}
OutterlineRenderer
OutterlineRenderer是目标GameObject的子GameObject,用来绘制轮廓。
- 提供CreateOutlineRenderer静态方法,target调用该静态方法来创建一个OutterlineRenderer子节点。
- FindOrCreateOutlineRenderer静态方法是对CreateOutlineRenderer的封装,对于已存在OutterlineRenderer则不再创建。
- 通过color属性,设置轮廓颜色
代码:
using UnityEngine;
using System.Collections;
public class OutterlineRenderer : MonoBehaviour
{
Material m_Material;
public RenderTexture m_RT;
public static OutterlineRenderer FindOrCreateOutlineRenderer(Unit target)
{
Transform t = target.transform.Find("OutterlineRenderer");
if (t == null)
return CreateOutlineRenderer(target);
else
return t.GetComponent<OutterlineRenderer>();
}
public static OutterlineRenderer CreateOutlineRenderer(Unit target)
{
float x = target.bound.size.x * 2;
float y = target.bound.size.y * 2;
RenderTexture rt = EffectRTGenerator.Inst().CreateOutterline(target.gameObject, x, y);
string path = "Misc/OutterlineRenderer";
GameObject type = Resources.Load<GameObject>(path);
if (type == null)
{
rt.Release();
return null;
}
GameObject go = GameObject.Instantiate(type);
go.name = type.name;
go.GetComponent<OutterlineRenderer>().Init(rt, x, y, Color.white);
go.transform.parent = target.transform;
go.transform.localPosition = new Vector3(0, 0, 1f);
go.transform.localScale = Vector3.one;
go.transform.localEulerAngles = Vector3.zero;
return go.GetComponent<OutterlineRenderer>();
}
public Color color
{
get{
return gameObject.GetComponent<MeshRenderer>().material.color;
}
set{
gameObject.GetComponent<MeshRenderer>().material.color = value;
}
}
void Init(RenderTexture rt, float x, float y ,Color color)
{
Mesh mesh;
MeshRenderer renderer = gameObject.GetComponent<MeshRenderer>();
mesh = GetComponent<MeshFilter>().mesh;
/* 6 7
* 4 5
* 2 3
* 0 1
*/
int sectionCount = 1;
int[] triangles = new int[ sectionCount * 6];
Vector3[] vertices = new Vector3[ sectionCount * 4];
Color[] colors = new Color[ sectionCount * 4];
Vector2[] uv = new Vector2[ sectionCount * 4];
for (int i = 0; i < sectionCount; i++)
{
vertices[4 * i] = new Vector2(-x / 2, -y / 2);
vertices[4 * i + 1] = new Vector2(x / 2, -y / 2);
vertices[4 * i + 2] = new Vector2(-x / 2, y / 2);
vertices[4 * i + 3] = new Vector2(x / 2, y / 2);
colors[4 * i] = Color.white;
colors[4 * i + 1] = Color.white;
colors[4 * i + 2] = Color.white;
colors[4 * i + 3] = Color.white;
uv[4 * i] = new Vector2(0, 0);
uv[4 * i + 1] = new Vector2(1, 0);
uv[4 * i + 2] = new Vector2(0, 1);
uv[4 * i + 3] = new Vector2(1, 1);
}
for (int i = 0; i < sectionCount; i++)
{
triangles[6 * i] = (i * 4) + 0;
triangles[6 * i + 1] = (i * 4) + 3;
triangles[6 * i + 2] = (i * 4) + 1;
triangles[6 * i + 3] = (i * 4) + 0;
triangles[6 * i + 4] = (i * 4) + 2;
triangles[6 * i + 5] = (i * 4) + 3;
}
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.colors = colors;
mesh.uv = uv;
m_RT = rt;
m_Material = new Material(Shader.Find("Outterline/Render"));
m_Material.hideFlags = HideFlags.HideAndDontSave;
renderer.material = m_Material;
renderer.material.color = color;
renderer.material.mainTexture = rt;
}
void OnDestroy()
{
DestroyImmediate(m_Material);
DestroyImmediate(m_Material);
}
public void Hide()
{
gameObject.SetActive(false);
}
public void Show()
{
gameObject.SetActive(true);
}
}