//立方体纹理在之前被我整合到了第六章的内容里,不再赘述
高级纹理
渲染纹理
现代GPU允许把整个三维场景渲染到中间缓冲中,而不是帧缓冲当中,这个中间缓冲叫做渲染目标纹理(Render Target Texture RTT) ,与之对应的是多重渲染目标(Mutil-Render Target) MRT 将场景渲染到多个渲染目标纹理中。为此,Unity专门定义了一种纹理类型——渲染纹理(Render Texture)。其使用通常有两种方式:
- 创建渲染纹理,将某个摄像机的渲染目标设置成该渲染纹理,摄像机的渲染结果就会实时渲染到该纹理中
- 通过后期处理抓取当前屏幕图像,Unity将屏幕图像放到一张同等分辨率的渲染纹理中
使用渲染纹理实现镜子效果
通过一个额外的摄像机,调整到对应位置,设置渲染目标为一张渲染纹理,将该渲染纹理作为一张2D纹理,在采样是,将UV坐标的进行翻转
Shader "Unlit/镜子"
{
Properties{
_MainTex("MainTex",2D) = "white"{}
}
SubShader{
Tags {
"RenderType" = "Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex vert//哪个函数包含了顶点着色器的代码
#pragma fragment frag//哪个函数包含了片元着色器的代码
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
struct VertexInput { //输入结构
float4 vertex : POSITION;
float4 uv0:TEXCOORD0;
};
struct VertexOutput { //输出结构
float4 pos : SV_POSITION;
float4 uv:TEXCOORD0;
};
VertexOutput vert(VertexInput v) { //顶点Shader
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv0;
o.uv.x = 1 - o.uv.x; //将uv.x分量进行翻转,实现镜子效果
return o;
}
float4 frag(VertexOutput i) : COLOR { //像素Shader
return tex2D(_MainTex,i.uv);
}
ENDCG
}
}
FallBack "Diffuse"
}
物体和镜面和摄像机位置
通过调整摄像机角度,获得最佳效果
玻璃效果
Unity Shader中可以使用GrabPass完成对屏幕图像的抓取。定义GrabPass后,Unity将当前屏幕图像绘制在一张纹理中,使用GrabPass模拟玻璃透明效果,可以对物体后面的图像做更复杂的处理(使用法线模拟折射效果),而不是像使用透明度混合,只是颜色上的混合。 在使用GrabPass进行透明效果模拟时,要注意渲染顺序的设置 ,先保证场景中所有不透明物体已经绘制在屏幕上,再对屏幕进行抓取图像,因此一般设置成 "Queue"="Transparent"
实现玻璃效果:
●Step1 获取指定位置的立方体纹理,通过反射方向采样得到反射颜色
●Step2 获取屏幕抓取图像,通过法线纹理得到法线方向作为影响值与影响因子相乘,调整获取的屏幕图像扭曲程度来模拟折射
●Step3 将两者颜色进行混合,通过混合值调整反射和折射的混合程度
Shader "Unlit/玻璃"
{
Properties{
_CubeMap("Cubemap", Cube) = "_Skybox" {}
_MainTex("Main Tex",2D) = "white"{}
_BumpTex("Bump Tex",2D) = "bump"{}
_Distortion("Distortion",Range(0,100)) = 10
_RefractionAmount("RefractionAmount",Range(0,1)) = 1
}
SubShader{
Tags { "Queue" = "Transparent"
"RenderType" = "Opaque"}
GrabPass {"_RefractionTex"}
Pass {
Name "FORWARD"
Tags {
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
//输入参数
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpTex;
float4 _BumpTex_ST;
uniform samplerCUBE _CubeMap;
float _Distortion;
float _RefractAmount;
sampler2D _RefractionTex; //存储GrabPass抓取的屏幕图像
float4 _RefractionTex_TexelSize; //得到屏幕图像的纹素值,在做偏移计算时使用
//输入结构
struct VertexInput {
float4 vertex : POSITION; //顶点信息
float3 normal : NORMAL; //法线信息,用作之后法线采样与TBN矩阵的计算
float4 tangent:TANGENT;
float4 uv0:TEXCOORD0;
};
//输出结构
struct VertexOutput {
float4 pos : SV_POSITION; // 屏幕空间顶点位置
float4 uv : TEXCOORD0;
float4 posWS : TEXCOORD1; // 世界空间顶点位置
float3 nDirWS : TEXCOORD2; // 世界空间法线方向
float3 tDirWS : TEXCOORD3; // 世界空间切线方向
float3 bDirWS : TEXCOORD4; // 世界空间副切线方向
float4 scrPos :TEXCOORD5;
float3 vDirWS:TEXCOORD6;
};
//顶点Shader
VertexOutput vert(VertexInput v) {
VertexOutput o = (VertexOutput)0; //新建一个输出结构
o.pos = UnityObjectToClipPos(v.vertex); // 从模型空间转换到裁剪空间的顶点位置
o.scrPos = ComputeGrabScreenPos(o.pos);
o.uv.xy = v.uv0.xy * _MainTex_ST.xy + _MainTex_ST.zw; // 传递UV
o.uv.zw = v.uv0.xy * _BumpTex_ST.xy + _BumpTex_ST.zw;
o.posWS = mul(unity_ObjectToWorld, v.vertex); // 从模型空间转换到世界空间的顶点位置
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 从模型空间转换到世界空间的法线方向
o.tDirWS = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz); // 从模型空间转换到世界空间的切线方向
o.bDirWS = normalize(cross(o.nDirWS, o.tDirWS) * v.tangent.w); // 从模型空间转换到世界空间的副切线方向
o.vDirWS = UnityWorldSpaceViewDir(o.posWS);
return o; // 将输出结构 输出
}
//像素Shader
float4 frag(VertexOutput i) : COLOR{
float3x3 TBN = float3x3(i.tDirWS, i.bDirWS, i.nDirWS); // TBN矩阵
float3 nDirTS = UnpackNormal(tex2D(_BumpTex, i.uv.zw));//采集法线贴图
float2 offset = nDirTS * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = offset + i.scrPos.xy;
fixed3 refrColor = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb;
return fixed4(refrColor, 1.0); }
ENDCG
}
}
FallBack "Tranparent/VertexLit"
}
Shader "Custom/Chapter10_GlassRefraction" {
Properties{
_MainTex("Main Tex",2D)="white"{}
_BumpTex("Bump Tex",2D)="bump"{}
_CubeMap("Cube Map",Cube)="_Skybox"{}
_Distortion("Distortion",Range(0,100))=10
_RefractAmount("Refract Amount",Range(0.0,1.0))=1.0
}
SubShader{
Tags{"Queue"="Transparent" "RenderType"="Opaque"}
//指定渲染队列为"Transparent",确保所有不透明物体先渲染完成
GrabPass {"_RefractionTex"}
//声明GrabPass 该Pass会将屏幕抓取图像存储到名为"_RefractionTex"的纹理中
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpTex;
float4 _BumpTex_ST;
samplerCUBE _CubeMap;
float _Distortion;
float _RefractAmount;
sampler2D _RefractionTex; //存储GrabPass抓取的屏幕图像
float4 _RefractionTex_TexelSize; //得到屏幕图像的纹素值,在做偏移计算时使用
struct a2v{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 tangent:TANGENT;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
float4 scrPos:TEXCOORD1;
float4 TtoW0:TEXCOORD2;
float4 TtoW1:TEXCOORD3;
float4 TtoW2:TEXCOORD4;
};
v2f vert(a2v v){
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.scrPos=ComputeGrabScreenPos(o.pos);
o.uv.xy=TRANSFORM_TEX(v.vertex,_MainTex);
o.uv.zw=TRANSFORM_TEX(v.vertex,_BumpTex);
float3 worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
fixed3 worldNormal=UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent=UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal=cross(worldNormal,worldTangent)*v.tangent.w;
o.TtoW0=(worldTangent.x,worldBinormal.x,worldNormal.x,worldPos.x);
o.TtoW1=(worldTangent.y,worldBinormal.y,worldNormal.y,worldPos.y);
o.TtoW2=(worldTangent.z,worldBinormal.z,worldNormal.z,worldPos.z);
return o;
}
fixed4 frag(v2f i):SV_Target{
float3 worldPos=(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
fixed3 worldViewDir=normalize(UnityWorldSpaceViewDir(worldPos));
fixed3 bump=UnpackNormal(tex2D(_BumpTex,i.uv.zw));
float2 offset=bump*_Distortion*_RefractionTex_TexelSize.xy;
i.scrPos.xy=offset+i.scrPos.xy;
fixed3 refrColor=tex2D(_RefractionTex,i.scrPos.xy/i.scrPos.w).rgb;
bump=normalize(half3(dot(i.TtoW0.xyz,bump),dot(i.TtoW1.xyz,bump),dot(i.TtoW2.xyz,bump)));
fixed3 reflectDir=reflect(-worldViewDir,bump);
fixed4 texColor=tex2D(_MainTex,i.uv.xy);
fixed3 reflColor=texCUBE(_CubeMap,reflectDir).rgb*texColor.rgb;
fixed3 finalColor=reflColor*(1-_RefractAmount)+refrColor*_RefractAmount;
return fixed4(finalColor,1.0);
}
ENDCG
}
}
FallBack "Tranparent/VertexLit"
}
/两个对法线部分处理不一样,第一个没办法设置cubemap透明度。第二个为源码
程序纹理
程序纹理是通过计算机计算生成的图像,使用特定的算法创建个性化图案或非常真实的自然元素。
[ExecuteInEditMode]
public class ProceduralTextureGeneration : MonoBehaviour {//脚本名称一定要对应
public Material material = null;
#region Material properties
[SerializeField,SetProperty("textureWidth")]
private int m_textureWidth = 512;
public int textureWidth {
get {
return m_textureWidth;
}
set {
m_textureWidth = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("backgroundColor")]
private Color m_backgroundColor = Color.white;
public Color backgroundColor {
get {
return m_backgroundColor;
}
set {
m_backgroundColor = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("circleColor")]
private Color m_circleColor = Color.yellow;
public Color circleColor {
get {
return m_circleColor;
}
set {
m_circleColor = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("blurFactor")]
private float m_blurFactor = 2.0f;
public float blurFactor {
get {
return m_blurFactor;
}
set {
m_blurFactor = value;
_UpdateMaterial();
}
}
#endregion
private Texture2D m_generateTexture = null;
// Use this for initialization
void Start () {
if (material == null)
{
Renderer renderers = gameObject.GetComponent<Renderer>();
if (renderers == null)
{
Debug.LogWarning("Cannot find a renderer.");
return;
}
material = GetComponent<Renderer>().sharedMaterial;
}
_UpdateMaterial();
}
// Update is called once per frame
private void _UpdateMaterial() {
if (material != null)
{
m_generateTexture = _GenerateProceduralTexture();
material.SetTexture("_MainTex",m_generateTexture);
}
}
private Texture2D _GenerateProceduralTexture()
{
Texture2D proceduralTexture=new Texture2D(textureWidth,textureWidth);
//定义圆与圆之间的距离
float circleInterval = textureWidth/4.0f;
//定义圆的半径
float radius = textureWidth/10.0f;
//定义模糊系数
float edgeBlur = 1.0f/blurFactor;
for (int w = 0; w < textureWidth; w++)
{
for (int h = 0; h < textureWidth; h++)
{
Color pixel = backgroundColor;
//绘制9个圆
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
//计算当前所绘制圆的位置
Vector2 circleCenter=new Vector2(circleInterval*(i+1),circleInterval*(j+1));
//计算当前像素与圆边界的距离
float dist = Vector2.Distance(new Vector2(w, h), circleCenter)-radius;
//模糊圆的边界
Color color = _MixColor(circleColor, new Color(pixel.r,pixel.g,pixel.b,0.0f),Mathf.SmoothStep(0f,1f,dist*edgeBlur));
//与之前得到的颜色混合
pixel = _MixColor(pixel, color, color.a);
}
}
proceduralTexture.SetPixel(w,h,pixel);
}
}
proceduralTexture.Apply();
return proceduralTexture;
}
private Color _MixColor(Color color0, Color color1, float mixFactor)
{
Color mixColor = Color.white;
mixColor.r = Mathf.Lerp(color0.r, color1.r, mixFactor);
mixColor.g = Mathf.Lerp(color0.g, color1.g, mixFactor);
mixColor.b = Mathf.Lerp(color0.b, color1.b, mixFactor);
mixColor.a = Mathf.Lerp(color0.a, color1.a, mixFactor);
return mixColor;
}
}