模拟spotlight

 

实现方式:将聚光灯颜色按与光源的距离叠加到光照模型上,阴影用shadowmap:在光源处放一个相机,生成一张深度图和转换矩阵,再用主相机渲染出的片元的深度余存储的深度相比,小于的是阴影。

贴图数组:(18条消息) Texture2DArray 功能测试_上午八点的博客-CSDN博客_texture2darray

///shader
///功能: 描边
Shader "Custom/DailyGameHex"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_OutlineCol("OutlineCol", Color) = (1,0,0,1)
_OutlineFactor("OutlineFactor", Range(0,1)) = 0.1
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
LOD 100
//描边
Pass
{
//剔除正面,只渲染背面,对于大多数模型适用,不过如果需要背面的,就有问题了
Cull Front
CGPROGRAM

//使用vert函数和frag函数
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _OutlineCol;
float _OutlineFactor;
struct v2f
{
float4 pos : SV_POSITION;
};

v2f vert(appdata_full v)
{
v2f o;
//在vertex阶段,每个顶点按照法线的方向偏移一部分,不过这种会造成近大远小的透视问题
//v.vertex.xyz += v.normal * _OutlineFactor;
o.pos = UnityObjectToClipPos(v.vertex);
//将法线方向转换到视空间
float3 vnormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
//将视空间法线xy坐标转化到投影空间,只有xy需要,z深度不需要了
float2 offset = TransformViewToProjection(vnormal.xy);
//在最终投影阶段输出进行偏移操作
o.pos.xy += offset * _OutlineFactor;
return o;
}

fixed4 frag(v2f i) : SV_Target
{
//这个Pass直接输出描边颜色
return _OutlineCol;
}

ENDCG

}
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fwdbase

#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : POSITION;
float3 normal : NORMAL;
float4 worldPos : TEXCOORD2;
SHADOW_COORDS(3)
};

sampler2D _MainTex;
float4 _MainTex_ST;

float4 SpotLightColor[5];//聚光灯颜色
float4 SpotLightPos[5];//聚光灯位置
float4 SpotLightDir[5];//聚光灯照射方向
float SpotLightAngle[5];//聚光灯角度
float SpotLightBrightness[5];//聚光灯亮度

float4x4 SHADOW_MAP_VP[5];//相机变换矩阵
//sampler2D ShadowMapTexture[5];//聚光灯阴影深度图
UNITY_DECLARE_TEX2DARRAY(ShadowMapTexture);//聚光灯阴影深度图
//聚光灯数量
int spotLightCount;

v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.normal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
TRANSFER_SHADOW(o);
return o;
}

fixed4 frag (v2f i) : SV_Target
{
// 平行光
fixed4 col = tex2D(_MainTex, i.uv);
float4 lightColor = _LightColor0;
float3 lightDir = WorldSpaceLightDir(i.worldPos);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos.xyz);
fixed4 finalCol = col * lightColor * saturate(dot(lightDir, i.normal)) * atten;

for(int index=0;index<spotLightCount;index++)
{
//聚光灯方向
float4 worldDir = SpotLightPos[index] - i.worldPos;
float3 spotLightDir = mul(unity_WorldToObject, worldDir).xyz;
spotLightDir = normalize(spotLightDir);
float3 downdir = normalize(mul(unity_WorldToObject,SpotLightDir[index].xyz));
float spotLight = max(SpotLightAngle[index] - abs(degrees(acos(dot(downdir, spotLightDir)))),0);
fixed4 secondCol = (col * 0.5 + SpotLightBrightness[index]) * (col * SpotLightColor[index] * spotLight * 0.01);

if(spotLight != 0)
{
//计算NDC坐标
fixed4 ndcpos = mul(SHADOW_MAP_VP[index] , i.worldPos);
ndcpos.xyz = ndcpos.xyz / ndcpos.w;
//从[-1,1]转换到[0,1]
float3 uvpos = ndcpos * 0.5 + 0.5;

//float depth = DecodeFloatRGBA(tex2D(ShadowMapTexture[i], uvpos.xy));
float depth = DecodeFloatRGBA(UNITY_SAMPLE_TEX2DARRAY(ShadowMapTexture, float3(uvpos.xy, index)));
secondCol = secondCol * step(depth, ndcpos.z + 0.00005);
}

finalCol = finalCol + secondCol;
}

return finalCol;
}

ENDCG
}

}
Fallback "Mobile/Diffuse"
}

///光源相机的shader,拿深度图

Shader "Hidden/ShadowMap" {
Properties {
}

SubShader {
Tags { "RenderType"="Opaque" }

/**/
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

struct v2f {
float4 vertex : POSITION;
float2 depth: TEXCOORD1;
};

float3 worldLightVector;

v2f vert(appdata_base v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.depth = o.vertex.zw;
return o;
}

fixed4 frag(v2f i) : SV_Target{
//discard;
float depth = i.depth.x / i.depth.y;
return EncodeFloatRGBA(depth);
}
ENDCG
}
}
}

 

c#

/// <summary>
/// 初始化建筑shader参数
/// </summary>
public IEnumerator DalayInitLightShaderSetting()
{
OpenOrCloseBakeCamera(true);
yield return new WaitForSeconds(0.5f);
int spotLightCount = listSpotLight.FindAll(sp => sp.gameObject.activeInHierarchy).Count;
renderMat.SetInt("spotLightCount", spotLightCount);
List<Matrix4x4> matrixList = new List<Matrix4x4>(spotLightCount);
List<RenderTexture> texList = new List<RenderTexture>(spotLightCount);
List<Vector4> dirList = new List<Vector4>(spotLightCount);
List<Vector4> posList = new List<Vector4>(spotLightCount);
List<float> angleList = new List<float>(spotLightCount);
List<Color> colorList = new List<Color>(spotLightCount);
List<float> brightnessList = new List<float>(spotLightCount);

for (int i=0;i< listSpotLight.Count; i++)
{
ShadowMapCamera spotLight = listSpotLight[i];
if (!spotLight.gameObject.activeInHierarchy)
continue;
//spotLight.Bake(true);
matrixList.Add(spotLight.GetShadowMapMatrix());
dirList.Add(spotLight.lightDir);
texList.Add(spotLight.GetDepathRenderTexture());
posList.Add(spotLight.transform.position);
angleList.Add(spotLight.lightAngle);
colorList.Add(spotLight.spotLightColor);
brightnessList.Add(spotLight.spotLightBrightness);
//spotLight.Bake(false);
}
//没有聚光灯不作处理
if(spotLightCount > 0)
{
//深度贴图和相机变换矩阵
if (texList.Count > 0)
{
Texture2DArray texArr = new Texture2DArray(depthRTSize, depthRTSize, texList.Count, TextureFormat.RGBA32, false, false);
for (int i = 0; i < texList.Count; i++)
{
Graphics.CopyTexture(texList[i], 0, 0, texArr, i, 0);
}
renderMat.SetTexture("ShadowMapTexture", texArr);
renderMat.SetMatrixArray("SHADOW_MAP_VP", matrixList);
}

renderMat.SetVectorArray("SpotLightDir", dirList);
renderMat.SetVectorArray("SpotLightPos", posList);
renderMat.SetFloatArray("SpotLightAngle", angleList);
renderMat.SetColorArray("SpotLightColor", colorList);
renderMat.SetFloatArray("SpotLightBrightness", brightnessList);
}


OpenOrCloseBakeCamera(false);
}

posted @ 2022-09-21 19:38  mc宇少  阅读(29)  评论(0编辑  收藏  举报