因为之前想给一些Shader测试环境反射效果。
但又没有相关的Cubemap来做测试。
所以只好去找相关的Cubemap的创建方法。
Unity 实现快照当前场景到Cubemap,在从Cubemap中读取每个面的纹理
使用现成Mobile/Skybox/6 Sided就需要提取Cubemap的纹理来设置每个面的纹理
使用现成Mobile/Skybox/Cubemap就不需要提取了,直接用该Cubemap设置材质的Cubemap
using UnityEngine;
public class CubemapSnapshot : MonoBehaviour
{
public Camera cam;
public Cubemap cubemap;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// camera可以newZ一个临时用的GameObject.AddComponent();
// RenderToCubemap完后,可以将此GameObject destroy掉就可以了
// 这里我就懒得这么写了
// 另外,要确保Cubemap的readable是勾上的
// 部分Unity版本勾上readable就会导致unity崩溃
// 改用Unity 2019.1.7f1就没崩了,用最新的unity就好
cam.RenderToCubemap(cubemap);
Debug.Log($"Cubemap snapshot complete!");
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
// 提取Cubemap中的Textures
// 但提取后,法线up,down搞反了(其实是Cubemap类的GetPixels(CubemapFaces) API的BUG)
// 你可以新建Materials,选Mobile/Skybox/6 sided的方式来确认就知道了,我自己试过
// 然后将这个材质设置为Scene的Skybox
// 当然你可以直接用Mobile/Skybox/Cubemap方式就好了,就不用提取6 sided了
public class PickupCubemapTex
{
[MenuItem("Cubemap/PickupTex")]
public static void PickupTex()
{
var obj = Selection.activeObject;
var cubemap = obj as Cubemap;
if (cubemap == null)
{
Debug.LogWarning($"Selecting one cubemap plz.");
return;
}
var idx = (int)CubemapFace.PositiveX;
var count = (int)CubemapFace.NegativeZ + 1;
List<PickupCubemapTexInfo> texs = new List<PickupCubemapTexInfo>();
Dictionary<CubemapFace, string> nameMap = new Dictionary<CubemapFace, string>();
nameMap[CubemapFace.PositiveX] = "Right";
nameMap[CubemapFace.NegativeX] = "Left";
nameMap[CubemapFace.PositiveY] = "Upwards";
nameMap[CubemapFace.NegativeY] = "Downward";
nameMap[CubemapFace.PositiveZ] = "Forward";
nameMap[CubemapFace.NegativeZ] = "Backward";
try
{
var title = "Select the directory which stored CubeMap Tex.";
var filepath = AssetDatabase.GetAssetPath(obj);
var folder = Directory.GetParent(filepath);
var foldername = EditorUtility.OpenFolderPanel(title, folder.ToString(), string.Empty);
if (string.IsNullOrEmpty(foldername) || !Directory.Exists(foldername))
{
// noop
}
else
{
for (; idx < count; idx++)
{
var face = (CubemapFace)idx;
var ps = cubemap.GetPixels(face);
var newTex = new Texture2D(1024, 1024); // 注意这里的texture2d 的width, height对应cubemap中的face size,但类中没定义,所以这里匹配好你的cubemap来使用就好了
newTex.SetPixels(ps);
texs.Add(new PickupCubemapTexInfo { tex = newTex, name = nameMap[face] });
}
foldername = foldername.Replace("\\", "/");
var cd = Directory.GetParent(Path.GetFullPath("Assets")).FullName;
cd = cd.Replace("\\", "/");
Debug.Log($"cd:{cd}");
foldername = Path.Combine(foldername, obj.name);
if (!Directory.Exists(foldername))
{
Directory.CreateDirectory(foldername);
}
foldername = foldername.Replace("\\", "/");
Debug.Log($"append assetsname folder:{foldername}");
foreach (var item in texs)
{
var fullname = Path.Combine(foldername, item.name + ".tga");
fullname = fullname.Replace("\\", "/");
fullname = fullname.Replace(cd + "/", "");
Debug.Log($"fullname:{fullname}");
var bs = item.tex.EncodeToTGA();
var fs = File.Open(fullname, FileMode.Create, FileAccess.Write);
fs.Write(bs, 0, bs.Length);
fs.Close();
fs.Dispose();
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
catch (Exception er)
{
Debug.LogError(er);
}
finally
{
foreach (var item in texs)
{
Texture2D.DestroyImmediate(item.tex);
}
texs.Clear();
}
}
}
public class PickupCubemapTexInfo
{
public Texture2D tex;
public string name;
}
注意:Inspector中的Face size决定new Texture2D的大小。这里做测试才使用这么大,项目中为达到环境反射效果,设置到适合就行
先选中上面快照出来的Cubemap
选择菜单Cubemap->PickupCubemapTex
我们再新建一个Material,Shader设置为Skybox/6 sided,设置好对应的纹理sided
我们可以看到右下角,显示的,天地倒转了,而且这里头需要将我们将Pickup出来的up,down设置到skybox/6 sided材质中是,需要将up/down掉转来设置,有可能是Cubemap类的GetPixels(CubemapFaces) API的BUG,导致提取出来的纹理上,下都搞反了,因为用的人少,所以bug没在unity的report有人去提,这里我只是刚好测试用到了一下就发现了。(当然,又或者是我的使用姿势不对。)
最终将该6 sided用到scene的skybox设置中查看,顺便我们也将角色的环境反射shader测试了一下直接反射Cubemap(不是6 sided),效果是正确的,建议使用skybox/cubemap的shader而不是6 sided的
这个是官方的SurfaceShader例子中的代码
Shader "Example/WorldRefl" {
Properties{
_MainTex("Texture", 2D) = "white" {}
_Cube("Cubemap", CUBE) = "" {}
}
SubShader{
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float3 worldRefl;
};
sampler2D _MainTex;
samplerCUBE _Cube;
void surf(Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb * 0.5;
o.Emission = texCUBE(_Cube, IN.worldRefl).rgb;
}
ENDCG
}
Fallback "Diffuse"
}
主要看:samplerCUBE _ReflectCube的采样的地方就大概知道如何使用了
//jave.lin 2019.06.17
//simple illumination
Shader "Custom/MyReflection"
{
Properties{
[NoScaleOffset] _MainTex("Main Tex", 2D) = "white" {}
[NoScaleOffset] _ReflectCube("Reflect Cube", Cube) = "white" {}
_ReflectIntensity("Reflection Intensity", Range(0,1)) = 0.5
_ReflectBrightness("Reflection Brightness", Range(1,2)) = 1
_SpecularIntensity("Specular Intensity", Range(0,1)) = 1
_AmbientIntensity("Ambient Intensity", Range(0,1)) = 1
_SelfShadowColor("Self Shadow Color", Color) = (0.5,0.5,0.5,0.5)
[Toggle] _HalfLambert("Half Lambert", Float) = 1
}
SubShader{
Pass{
Tags {"Queue" = "Opaque" "LightMode"="ForwardBase"} // shadow setup1:"LightMode"="ForwardBase"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase // shadow setup2:fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc" // shadow setup3:#include "Lighting.cginc"
#include "AutoLight.cginc" // shadow setup4:#include "AutoLight.cginc"
struct a2v{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
fixed4 tc : TEXCOORD1;
LIGHTING_COORDS(2,3) // shadow setup5:LIGHTING_COORDS(n,n+1)
float3 worldNormal : TEXCOORD4;
float3 worldPos : TEXCOORD5;
float3 eyeDir : TEXCOORD6;
float3 lightDir : TEXCOORD7;
};
sampler2D _MainTex;
samplerCUBE _ReflectCube;
float _ReflectIntensity;
float _ReflectBrightness;
float _SpecularIntensity;
float _AmbientIntensity;
fixed4 _SelfShadowColor;
bool _HalfLambert;
v2f vert(a2v v){
v2f o = (v2f)0;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.eyeDir = normalize(_WorldSpaceCameraPos.xyz - o.worldPos).xyz;
o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
TRANSFER_VERTEX_TO_FRAGMENT(o); // shadow setup6:TRANSFER_VERTEX_TO_FRAGMENT(o)
return o;
}
fixed4 frag(v2f i):SV_TARGET{
float3 normalDir = normalize(i.worldNormal);
float3 reflectDir = reflect(-i.eyeDir,normalDir);
fixed3 specularDir = reflect(-i.lightDir,normalDir);
fixed4 c = tex2D(_MainTex, i.uv); // albedo
fixed4 r = texCUBE(_ReflectCube, reflectDir); // enviroment cubemap
if(_HalfLambert){
c.xyz *= (dot(i.lightDir,normalDir) + 1) * 0.5; // diffuse
}else{
c.xyz *= dot(i.lightDir,normalDir); // diffuse
}
c.xyz += c.xyz * UNITY_LIGHTMODEL_AMBIENT.rgb; // ambient
float specularDotEye = max(0, dot(specularDir, i.eyeDir));
c.xyz += (_LightColor0.rgb) * specularDotEye * _SpecularIntensity; // specular
fixed3 skyDir = reflect(float3(0,-1,0),normalDir);
c.xyz += unity_AmbientSky.rgb * unity_AmbientSky.a * max(0, dot(skyDir, i.eyeDir)) * _AmbientIntensity; // ambient sky
fixed3 equatorDir = normalize(float3(_WorldSpaceCameraPos.x, i.worldPos.y, _WorldSpaceCameraPos.z) - i.worldPos).xyz;
equatorDir = reflect(-equatorDir,normalDir);
c.xyz += unity_AmbientEquator.rgb * unity_AmbientEquator.a * max(0, dot(equatorDir, i.eyeDir)) * _AmbientIntensity; // ambient equator
fixed3 groundDir = reflect(float3(0,1,0),normalDir);
c.xyz += unity_AmbientGround.rgb * unity_AmbientGround.a * max(0, dot(groundDir, i.eyeDir)) * _AmbientIntensity; // ambient ground
c.xyz = lerp(c.xyz,c.xyz*r.xyz*_ReflectBrightness,_ReflectIntensity); // reflection
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos); // shadow setup7:UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos)
atten = (atten + 1) * 0.5;
c.xyz *= atten; // lighting and shadow attenuation
return c;
}
ENDCG
}
}
Fallback "Diffuse"
}
SurfaceShaderTest_SampleScene_测试环境反射 提取码: 3sng
unity version : 2019.1.7f1 运行SampleScene
SurfaceShader玩玩就好,不必用于项目中,一般都是类似PBR材质才用,除非你知道SurfaceShader的所有功能刚好都是你需要的,否则还是不要用。
因为SurfaceShader编译出来的vs/ps shader挺多的,功能臃肿,项目中还是自己写vs/ps好。
【Unity Shader】 CubeMap(立方体贴图) - 里头还有折射,菲涅尔的概念