Unity Shader学习:体积光/体积阴影

在前向渲染下实现平行光的体积光影效果,需要全屏深度图,延迟渲染会更划算。

思路:通过ray marching的步进点位置计算该点是否在阴影中,采样阴影贴图,通过dither+blur优化性能,叠加原图和光影图。步进策略直接采用等距,有其他策略性能表现也会更好,阴影级联 (Shadow Cascade)选择了无,注意如果开启了多个的话采样阴影算法需要调整。

参考:
https://blog.csdn.net/puppet_master/article/details/79859678
https://zhuanlan.zhihu.com/p/37624886
https://catlikecoding.com/unity/tutorials/rendering/part-7/

原图:

体积光:

体积阴影:

体积光+体积阴影

从上方打光:

c#部分:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class VolumetricShadow : MonoBehaviour {private Matrix4x4 frustumCorners = Matrix4x4.identity;private Transform camTransform;private Camera cam;private RenderTexture marchingRT;private RenderTexture tempRT;public Material mat;[Range(0,5)]public int downSample=2;[Range(0f, 5f)]public float samplerScale = 1f;[Range(0,256)]public int rayMarchingStep=16;[Range(0f,100f)]public float maxRayLength=15f;[Range(0f, 2f)]public float volumetricLightIntenstiy = 0.05f;[Range(0f, 2f)]public float lightScatteringFactor = 0.5f;[Range(0f, 5f)]public float volumetricShadowIntenstiy = 0f;[Range(0f, 0.1f)]public float shadowAttenuation = 0.08f;[Range(0f, 1f)]public float minShadow = 0.5f;void Start () {camTransform = transform;cam = GetComponent<Camera>();cam.depthTextureMode = DepthTextureMode.Depth;mat.SetTexture("_DitherMap", GenerateDitherMap());}private void OnRenderImage(RenderTexture source, RenderTexture destination){        //field of viewfloat fov = cam.fieldOfView;//近裁面距离float near = cam.nearClipPlane;//横纵比float aspect = cam.aspect;//近裁面一半的高度float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);//向上和向右的向量Vector3 toRight = cam.transform.right * halfHeight * aspect;Vector3 toTop = cam.transform.up * halfHeight;//分别得到相机到近裁面四个角的向量//depth/dist=near/|topLeft|//dist=depth*(|TL|/near)//scale=|TL|/nearVector3 topLeft = camTransform.forward * near + toTop - toRight;float scale = topLeft.magnitude / near;topLeft.Normalize();topLeft *= scale;Vector3 topRight = camTransform.forward * near + toTop + toRight;topRight.Normalize();topRight *= scale;Vector3 bottomLeft = camTransform.forward * near - toTop - toRight;bottomLeft.Normalize();bottomLeft *= scale;Vector3 bottomRight = camTransform.forward * near - toTop + toRight;bottomRight.Normalize();bottomRight *= scale;frustumCorners.SetRow(0, bottomLeft);frustumCorners.SetRow(1, bottomRight);frustumCorners.SetRow(3, topRight);frustumCorners.SetRow(2, topLeft);mat.SetMatrix("_FrustumCornorsRay", frustumCorners);mat.SetInt("_RayMarchingStep", rayMarchingStep);mat.SetFloat("_MaxRayLength", maxRayLength);mat.SetFloat("_VolumetricLightIntensity", volumetricLightIntenstiy);mat.SetFloat("_VolumetricShadowIntenstiy", volumetricShadowIntenstiy);mat.SetFloat("_ScatteringFactor", lightScatteringFactor);mat.SetFloat("_MinShadow", minShadow);mat.SetFloat("_ShadowAttenuation", shadowAttenuation);marchingRT = RenderTexture.GetTemporary(Screen.width >> downSample, Screen.height >> downSample, 0, source.format);tempRT = RenderTexture.GetTemporary(Screen.width >> downSample, Screen.height >> downSample, 0, source.format);//计算阴影Graphics.Blit(source, marchingRT, mat, 0);//模糊阴影信息mat.SetVector("_Offsets", new Vector4(0, samplerScale, 0, 0));Graphics.Blit(marchingRT, tempRT,mat,1);mat.SetVector("_Offsets", new Vector4(samplerScale, 0, 0, 0));Graphics.Blit(tempRT, marchingRT, mat, 1);mat.SetVector("_Offsets", new Vector4(0, samplerScale, 0, 0));Graphics.Blit(marchingRT, tempRT, mat, 1);mat.SetVector("_Offsets", new Vector4(samplerScale, 0, 0, 0));Graphics.Blit(tempRT, marchingRT, mat, 1);//合并mat.SetTexture("_MarchingTex", marchingRT);Graphics.Blit(source, destination, mat, 2);RenderTexture.ReleaseTemporary(marchingRT);RenderTexture.ReleaseTemporary(tempRT);}//Guerrilla Games 分享 DitherMapprivate Texture2D GenerateDitherMap(){int texSize = 4;Texture2D ditherMap = new Texture2D(texSize, texSize, TextureFormat.Alpha8, false, true);ditherMap.filterMode = FilterMode.Point;Color32[] colors = new Color32[texSize * texSize];colors[0] = GetDitherColor(0.0f);colors[1] = GetDitherColor(8.0f);colors[2] = GetDitherColor(2.0f);colors[3] = GetDitherColor(10.0f);colors[4] = GetDitherColor(12.0f);colors[5] = GetDitherColor(4.0f);colors[6] = GetDitherColor(14.0f);colors[7] = GetDitherColor(6.0f);colors[8] = GetDitherColor(3.0f);colors[9] = GetDitherColor(11.0f);colors[10] = GetDitherColor(1.0f);colors[11] = GetDitherColor(9.0f);colors[12] = GetDitherColor(15.0f);colors[13] = GetDitherColor(7.0f);colors[14] = GetDitherColor(13.0f);colors[15] = GetDitherColor(5.0f);ditherMap.SetPixels32(colors);ditherMap.Apply();return ditherMap;}private Color32 GetDitherColor(float value){byte byteValue = (byte)(value / 16.0f * 255);return new Color32(byteValue, byteValue, byteValue, byteValue);}
}

shader部分:

Shader "Unlit/VolumetricShadow"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{//1.ray marching && get shadow infoPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "AutoLight.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float4 interpolatedRay:TEXCOORD2;};sampler2D _MainTex;float4 _MainTex_ST;          float _ScatteringFactor;float4x4 _FrustumCornorsRay;sampler2D _CameraDepthTexture;sampler2D _ShadowMapTexture;sampler2D _DitherMap;int _RayMarchingStep;float _MaxRayLength;float _VolumetricLightIntensity;float _VolumetricShadowIntenstiy;float _MinShadow;      float _ShadowAttenuation;//重映射float Remap(float x,float from1,float to1,float from2,float to2) {return (x - from1) / (to1 - from1) * (to2 - from2) + from2;}//判断该点是否在阴影float2 GetShadow(float3 worldPos) {//比较灯光空间深度float4 lightPos = mul(unity_WorldToShadow[0], float4(worldPos, 1));float shadow = UNITY_SAMPLE_DEPTH(tex2Dlod(_ShadowMapTexture, float4(lightPos.xy,0,0)));float depth = lightPos.z ;float shadowValue = step(shadow, depth);//阴影的衰减float dis = abs(depth - shadow);                              shadowValue += clamp(Remap(dis, _ShadowAttenuation,0.1,0,1),0,1)*(1-shadowValue);return shadowValue;}v2f vert(appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);//四个顶点对应的相机近裁面向量int index = step(0.5, v.uv.x) + step(0.5, v.uv.y)*2;//int index = 0;/*if (v.uv.x < 0.5&&v.uv.y < 0.5){index = 0;}else if (v.uv.x > 0.5&&v.uv.y < 0.5) {index = 1;}else if (v.uv.x > 0.5&&v.uv.y > 0.5) {index = 2;}else {index = 3;}*/o.interpolatedRay = _FrustumCornorsRay[index];    return o;}fixed4 frag (v2f i) : SV_Target{//获得世界坐标float depthTextureValue = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);float linearEyeDepth = LinearEyeDepth(depthTextureValue);//限制获取到的最远距离linearEyeDepth = clamp(linearEyeDepth,0, _MaxRayLength);float3 worldPos = _WorldSpaceCameraPos + linearEyeDepth * i.interpolatedRay.xyz;float vShadow = 1;float vLight = 0;float3 rayOri = _WorldSpaceCameraPos;                float3 rayDir = i.interpolatedRay.xyz;float disCam2World = length(worldPos - _WorldSpaceCameraPos);//dither扰动采样点float2 offsetUV = fmod(floor(i.vertex.xy), 4.0);float ditherValue = tex2D(_DitherMap, offsetUV*0.25).a;rayOri += ditherValue * rayDir;//防止背光时也产生影响float3 toLight = normalize(_WorldSpaceLightPos0);float dotLightRayDir = dot(toLight, rayDir)*0.5 + 0.5;              float scatteringLight = smoothstep(0.5, 1, dotLightRayDir);float3 currentPos;//固定的步数得到步长float marchStep = disCam2World / _RayMarchingStep;UNITY_LOOPfor (int j = 0; j < _RayMarchingStep; j++){                 currentPos = rayOri + i.interpolatedRay.xyz * marchStep * j;float disCam2Current = length(currentPos- _WorldSpaceCameraPos);//对比光线是否超过了深度float outOfRange = step(disCam2Current, disCam2World);//if (disCam2World>disCam2Current)//{                 float getShadow = GetShadow(currentPos);vShadow -= (1- getShadow) * _VolumetricShadowIntenstiy  / _RayMarchingStep * (j+3)/_RayMarchingStep * outOfRange;vLight += getShadow * _VolumetricLightIntensity * scatteringLight / _RayMarchingStep * (j-3)/_RayMarchingStep * outOfRange;//}//else//{// break;//}}vShadow = clamp(vShadow, _MinShadow, 1);vLight = pow(clamp(vLight, 0, 1),_ScatteringFactor);float4 col = float4(vLight, vShadow, 0, 1);                return col;}ENDCG}//2.blurPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float4 vertex : SV_POSITION;float2 uv : TEXCOORD0;float4 uv01 : TEXCOORD1;float4 uv23 : TEXCOORD2;float4 uv45 : TEXCOORD3;};sampler2D _MainTex;float4 _MainTex_TexelSize;float4 _Offsets;v2f vert(appdata v) {v2f o;_Offsets *= _MainTex_TexelSize.xyxy;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;o.uv01 = v.uv.xyxy + _Offsets.xyxy*float4(1, 1, -1, -1);o.uv23 = v.uv.xyxy + _Offsets.xyxy*float4(1, 1, -1, -1)*2.0;o.uv45 = v.uv.xyxy + _Offsets.xyxy*float4(1, 1, -1, -1)*3.0;return o;}float4 frag(v2f i) :SV_Target{float4 color = float4(0,0,0,0);color += 0.40*tex2D(_MainTex, i.uv);color += 0.15*tex2D(_MainTex, i.uv01.xy);color += 0.15*tex2D(_MainTex, i.uv01.zw);color += 0.10*tex2D(_MainTex, i.uv23.xy);color += 0.10*tex2D(_MainTex, i.uv23.zw);color += 0.05*tex2D(_MainTex, i.uv45.xy);color += 0.05*tex2D(_MainTex, i.uv45.zw);return color;}ENDCG}//3.combinePass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float4 vertex : SV_POSITION;float2 uv : TEXCOORD0;};sampler2D _MainTex;sampler2D _MarchingTex;v2f vert(appdata v) {v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;return o;}float4 frag(v2f i) :SV_Target{float4 finalColor = 1;float4 ori = tex2D(_MainTex,i.uv);float4 marching = tex2D(_MarchingTex, i.uv);finalColor.rgb = clamp(ori.rgb + marching.r*_LightColor0.rgb,0,1) * marching.g;return finalColor;}ENDCG}}
}

Unity Shader学习:体积光/体积阴影相关推荐

  1. Unity Shader 学习笔记(3)URP渲染管线带阴影PBR-Shader模板(ASE优化版本)

    此 Shader 已经不是最新版本,最新版本见本专栏的第四篇文章: Unity Shader 学习笔记(4) 材质面板截图: 功能实现(URP渲染管线下): PBR材质.投射和接收阴影. 代码展示: ...

  2. Unity Shader 学习笔记(27)渲染轮廓线(描边)方法、卡通风格渲染、素描风格渲染

    Unity Shader 学习笔记(27)渲染轮廓线(描边)方法.卡通风格渲染.素描风格渲染 参考书籍:<Unity Shader 入门精要> 渲染轮廓线(描边) 五种方法: 基于观察角度 ...

  3. Unity Shader学习:素描效果

    Unity Shader学习:素描效果 这是乐乐大佬书里的非真实渲染,其中的算法还是挺有意思的,感兴趣的小伙伴可以试一试. 素描效果基本原理:先将物体进行描边画出轮廓,计算物体的漫反射部分,漫反射越暗 ...

  4. Unity Shader 学习笔记(33) 全局光照(GI)、反射探针、线性空间和伽马空间、高动态范围(HDR)

    Unity Shader 学习笔记(33) 全局光照(GI).反射探针.线性空间和伽马空间.高动态范围(HDR) 参考书籍:<Unity Shader 入门精要> [<Real-Ti ...

  5. Unity Shader学习-高光反射

    Unity Shader学习-高光反射 高光反射计算公式 高光反射 = 光源的色彩和强度 * 材质的高光反射系数 * pow(max(0,视角方向 · 反射方向),_Gloss) 视角方向 = ref ...

  6. Unity Shader学习:SSAO屏幕环境光遮蔽

    Unity Shader学习:SSAO屏幕环境光遮蔽 主要思路:1.随机采样像素法线半球周围的像素,平均对比与该像素深度是否处在暗处.2.双边滤波去噪点.3.后期AO图与原图混合. 原文链接:http ...

  7. Unity Shader学习:动态模糊(shutter angle方式)

    Unity Shader学习:动态模糊 动态模糊一般有帧混合和motion vector两种,这里主要介绍motion vector的方法. Keijiro源码:https://github.com/ ...

  8. Unity Shader学习:水墨效果

    Unity Shader学习:水墨效果 偶然在网上看到9级铁甲蛹大神的水墨风格后处理觉得挺有意思,参照着实现一下,还是涉及到之前油画效果的算法,叫什么滤波暂时不清楚,应该用来处理手绘效果挺多的. 水墨 ...

  9. Unity Shader学习:SSR屏幕空间反射

    Unity Shader学习:SSR屏幕空间反射 本文在前向渲染模式下实现,延迟渲染更适合SSR,这里只简单的实现下,未作更深入的优化. 思路:沿视线和法线的反射向量步进光线,判断打到物体(这里用的是 ...

最新文章

  1. 完整的虚幻引擎超级课程:从初学者到专家
  2. 探讨 | 目前SLAM存在的问题
  3. 读郭老师推荐书籍--《原则》
  4. python的高级特性:切片,迭代,列表生成式,生成器,迭代器
  5. 中国.NET开发者峰会特别活动-基于k8s的微服务和CI/CD动手实践报名
  6. 从创业失败中学到的七条教训
  7. pip download timeout 下载慢,超时解决方法
  8. maven release插件将一版本发布到仓库中时Return code is: 401, ReasonPhrase:Unauthorized
  9. (Oracle)rownum用法详解 转载的
  10. 16.企业应用架构模式 --- 离线并发模式
  11. Flutter跨组件共享状态的利器Provider原理解析
  12. 安徽大学大学计算机网络教学平台,安徽大学-网络教学综合平台.DOC
  13. 常见比特和比特率单位换算
  14. Java关于跨年周数计算的问题解释,以及解决办法(附代码+图)
  15. php奖状,利用CSS布局做一个简单的荣誉证书(代码示例)
  16. Zabbix调优不完全指南
  17. 在vue中使用element-ui二次封装面包屑导条
  18. Numpy学习之——numpy.mean中axis参数用法
  19. connection_reset -101
  20. Unity发布小游戏(五):小游戏的打包与上传到CCD云服务器

热门文章

  1. jmeter分布式执行远程机报错,提示“Engine is busy – please try later”
  2. 第一章 略说中医的学习与研究(4)
  3. Android Camera驱动分析
  4. OpenWrt的多WAN和静态路由设置
  5. 介绍计算机专业说明文,介绍电脑的说明文作文
  6. 织梦dedecms响应式精密机械模具公司网站模板(自适应手机移动端)
  7. python 如果文件夹不存在则新建
  8. 计算机专业去支教学到什么,支教的收获及感悟4篇_大学生支教感想
  9. 企业自动化办公之请假单-下篇
  10. 百度之星1005 序列变换(lis)