Cubemap是环境映射的一种实现方法,使用三维纹理坐标进行采样,这个三维纹理坐标代表世界空间下的一个3D方向,这个方向矢量从立方体中心出发,采样的结果由交点计算得出。
缺点:1.当场景引入新的物体、光源或物体发生移动时,都要重新计算;2.仅可以反射环境,不可以反射使用了Cubemap的物体自身,因为Cubemap不能模拟多次反射,如两个金属球互相反射(全局光照可以实现),因此尽量对凸面体使用,不要对凹面体使用,因为凹面体会反射自身。
天空盒
1.新建材质;
2.在材质的unity shader下选择skybox/6 Sided;
3.把6张纹理的wrap mode设置为Clamp,防止接缝处出现不匹配的现象;
4.把材质赋给lighting中的skybox;
5.将camera中的clear flags设置为skybox;
6.如果希望某些摄像机使用不同的天空盒,可以在摄像机上单击component——rendering——skybox来完成覆盖;
创建立方体纹理
方法有三种:
1.准备一张HDR的立方体展开图,纹理类型设置cubemap;(推荐)
2.准备六张纹理贴到创建的Cubemap上;
3.脚本创建;
脚本创建通过Camera.RenderToCubemap实现:
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中,使用方法:创建gameobject,再创建cubemap(legacy->cubemap),勾选readable,最后打开Render into Cubemap功能渲染即可得到立方体纹理,face size代表渲染质量。
环境映射
反射
把前面得到的cubemap_0放到Reflection Cubemap属性中
// 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 ("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
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"
}
折射
斯涅尔定律:
η 1 s i n Θ 1 = η 2 s i n Θ 2 \eta _{1}sin\Theta _{1}=\eta _{2}sin\Theta _{2} η1sinΘ