什么是立方体纹理

  • 立方体纹理是环境映射的一种实现方式。

  • 一共包含6张图像

  • 好处:实现简单快速,效果好。

    缺点:场景中引入新的物体,光源,或者物体发生移动时需要重新生成。

    不能模拟多次反射的结果

  • 尽量对凸面体使用立方体纹理。

天空盒子

用于模拟背景的一种方式。

添加天空盒子:

  1. 新建材质,shader选择自带的Skybox/6 sided.
  2. 将图片附上,图片的wrap mode设置为clamp,
  3. 在window-rendering-lighting中的scene中将skybox设置为之前的材质
  4. 该设置会覆盖所有摄像机,如果希望使用不同的,需要给摄像机添加skybox组件。

创建环境映射的立方体纹理

  • 特殊布局的纹理创建

    需要类似立方体展开图或者全景布局图,将texture type设置为cubemap

  • 手动创建Cubemap

    上面的方法

  • 脚本生成

    通过unity提供的Camera.RenderToCubemap函数实现,该函数可以把任意位置的场景图像储存到6张图像中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    using UnityEngine;
    using UnityEditor;
    using System.Collections;

    public class RenderCubemapWizard : ScriptableWizard {

    public Transform renderFromPosition;
    public Cubemap cubemap;

    void OnWizardUpdate () {
    helpString = "Select transform to render from and cubemap to render into";
    isValid = (renderFromPosition != null) && (cubemap != null);
    }

    void OnWizardCreate () {
    // create temporary camera for rendering
    GameObject go = new GameObject( "CubemapCamera");
    go.AddComponent<Camera>();
    // place it on the object
    go.transform.position = renderFromPosition.position;
    // render into cubemap
    go.GetComponent<Camera>().RenderToCubemap(cubemap);

    // destroy temporary camera
    DestroyImmediate( go );
    }

    [MenuItem("GameObject/Render into Cubemap")]
    static void RenderCubemap () {
    ScriptableWizard.DisplayWizard<RenderCubemapWizard>(
    "Render cubemap", "Render!");
    }
    }

    通过调用该脚本就可以把一个位置的环境渲染到一个Cubemap当中。

反射

  • 通过视角方向的反射方向即可知道采样的位置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 10/Reflection" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
//控制反射颜色
_ReflectColor ("Reflection Color", Color) = (1, 1, 1, 1)
//控制反射程度
_ReflectAmount ("Reflect Amount", Range(0, 1)) = 1
//导入Cubemap
_Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}

Pass {
Tags { "LightMode"="ForwardBase" }

CGPROGRAM

#pragma multi_compile_fwdbase

#pragma vertex vert
#pragma fragment frag

#include "Lighting.cginc"
#include "AutoLight.cginc"

fixed4 _Color;
fixed4 _ReflectColor;
fixed _ReflectAmount;
samplerCUBE _Cubemap;

struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};

struct v2f {
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldViewDir : TEXCOORD2;
fixed3 worldRefl : TEXCOORD3;
SHADOW_COORDS(4)
};

v2f vert(a2v v) {
v2f o;

o.pos = UnityObjectToClipPos(v.vertex);

o.worldNormal = UnityObjectToWorldNormal(v.normal);

o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);

// Compute the reflect dir in world space
//通过视角方向计算反射方向,即得到指向环境的方向,通过这个方向在Cubemap中采样
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);

TRANSFER_SHADOW(o);

return o;
}

fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(i.worldViewDir);

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));

// Use the reflect dir in world space to access the cubemap
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;

UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

// Mix the diffuse color with the reflected color
//通过反射系数混合漫反射和反射,最后乘上光强衰减系数
fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount) * atten;

return fixed4(color, 1.0);
}

ENDCG
}
}
FallBack "Reflective/VertexLit"
}

折射

  • 斯涅尔定律(Snell’s Law)
    $$
    \eta_1sin\theta_1=\eta_2sin\theta_2
    $$
    一般来说需要计算两次折射:射入和射出

    但是实时渲染一般模拟1次折射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 10/Refraction" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_RefractColor ("Refraction Color", Color) = (1, 1, 1, 1)
//折射比率
_RefractAmount ("Refraction Amount", Range(0, 1)) = 1
//不同介质的透射比
_RefractRatio ("Refraction Ratio", Range(0.1, 1)) = 0.5
_Cubemap ("Refraction Cubemap", Cube) = "_Skybox" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}

Pass {
Tags { "LightMode"="ForwardBase" }

CGPROGRAM

#pragma multi_compile_fwdbase

#pragma vertex vert
#pragma fragment frag

#include "Lighting.cginc"
#include "AutoLight.cginc"

fixed4 _Color;
fixed4 _RefractColor;
float _RefractAmount;
fixed _RefractRatio;
samplerCUBE _Cubemap;

struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};

struct v2f {
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldViewDir : TEXCOORD2;
fixed3 worldRefr : TEXCOORD3;
SHADOW_COORDS(4)
};

v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);

o.worldNormal = UnityObjectToWorldNormal(v.normal);

o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);

// Compute the refract dir in world space
//需要入射方向,和法线,第三个两个介质的折射率之比,a->b,re=a/b
o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);

TRANSFER_SHADOW(o);

return o;
}

fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(i.worldViewDir);

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));

// Use the refract dir in world space to access the cubemap
fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb;

UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

// Mix the diffuse color with the refract color
fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount) * atten;

return fixed4(color, 1.0);
}

ENDCG
}
}
FallBack "Reflective/VertexLit"
}

菲涅尔反射

原理:当光线照射到物体表面时,一部分反射,一部分发生折射和散射,被反射的光和入射光存在一定的比率关系。

菲涅尔等式:

近似公式:Schlick 菲涅尔近似等式:
$$
F_{Schlick}(v,n)=F_0+(1-F_0)(1-v·n)^5
$$
F0是反射系数,控制反射强度,v是视角方向,n是法线

应用广泛的等式是Empricial菲涅尔近似等式:
$$
F_{Empricial}(v,n)=max(0,min(1,bias+scale\times(1-v·n)^{power}))
$$
三个英文是控制项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
//schlickj
Shader "Unity Shaders Book/Chapter 10/Fresnel" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_FresnelScale ("Fresnel Scale", Range(0, 1)) = 0.5
_Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}

Pass {
Tags { "LightMode"="ForwardBase" }

CGPROGRAM

#pragma multi_compile_fwdbase

#pragma vertex vert
#pragma fragment frag

#include "Lighting.cginc"
#include "AutoLight.cginc"

fixed4 _Color;
fixed _FresnelScale;
samplerCUBE _Cubemap;

struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};

struct v2f {
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldViewDir : TEXCOORD2;
fixed3 worldRefl : TEXCOORD3;
SHADOW_COORDS(4)
};

v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);

o.worldNormal = UnityObjectToWorldNormal(v.normal);

o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);

o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);

TRANSFER_SHADOW(o);

return o;
}

fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(i.worldViewDir);

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb;

fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);

fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));
//通过菲涅尔系数控制漫反射和反射的混合程度
fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;

return fixed4(color, 1.0);
}

ENDCG
}
}
FallBack "Reflective/VertexLit"
}

1
xxxxxxx