在Unity中实现屏幕空间反射Screen Space Reflection(2)
traceRay函数
在上一篇中,我们有如下签名的traceRay函数
bool traceRay(float3 start, float3 direction, out float2 hitPixel, out float3 debugCol ) {
}
其中的参数意义都很明了。start和direction是相机空间下的光线起点,以及光线方向。
traceRay的核心代码并不复杂,如下:
#define RAY_LENGTH 2.0
#define STEP_COUNT 64 //maximum sample count.
UNITY_LOOP //强制使用循环结构,不然就会代码5秒钟,编译1小时
for (int i = 1; i <= STEP_COUNT; i++) {
float3 p = start + (float)i/STEP_COUNT * RAY_LENGTH * direction ; //p是当前的光线的空间位置
float pDepth = p.z / -_ProjectionParams.z; //_ProjectionParams.z是far clip plane的值。又因为viewspace下正前方z值是负的,所以加个负号。
float4 screenCoord = mul(_Projection, float4(p,1)); //将光线投影到screen space中。
screenCoord /= screenCoord.w;
if (screenCoord.x < -1 || screenCoord.y < -1 || screenCoord.x > 1 || screenCoord.y > 1)
return false;
float camDepth = Linear01Depth(tex2Dlod(_CameraDepthTexture, float4(screenCoord.xy / 2 + 0.5,0,0))); //获取当前像素的深度。为了使用循环结构,这里必须用tex2Dlod而不是tex2D。
if (Intersect(pDepth,camDepth) ) { //相交检测
hitPixel = screenCoord.xy / 2 + 0.5;
debugCol = float3(hitPixel, 0);
return true;
}
}
相交检测
最简单的方式
最简单的,如果该像素的深度大于当前光线的深度(离相机更远),此时我们认为这是一个命中。
if (pDepth > camDepth) {
...
}
该种方法如上图所示,可以看到物体的下方会有明显的“拖影”。
加入厚度
为了改进效果,我们加入一个像素厚度的考量。当光线位于像素后面,并且不超出该像素的厚度时,才算命中。我们往往给像素一个固定的厚度。
if (pDepth > camDepth && pDepth < camDepth + 0.001 ) { //0.001是厚度
...
}
如图,拖影不见了。
获取像素实际的厚度
这种方法一般情况下就已经足够好了。如果要进一步改进的话,我们可以通过backface渲染,得到第二张深度贴图。通过将两张深度贴图的采样相减,得到一个像素的“厚度”。再按照这个厚度去做相交测试。
后处理脚本:
private void OnRenderImage(RenderTexture source, RenderTexture destination) {
RenderBackface();
mat.SetTexture("_BackfaceTex", GetBackfaceTexture());
mat.SetMatrix("_WorldToView", GetComponent<Camera>().worldToCameraMatrix);
Graphics.Blit(source, destination, mat,0);
}
private void RenderBackface() {
if (backfaceCamera == null) {
var t = new GameObject();
var mainCamera = Camera.main;
t.transform.SetParent(mainCamera.transform);
t.hideFlags = HideFlags.HideAndDontSave;
backfaceCamera = t.AddComponent<Camera>();
backfaceCamera.CopyFrom(mainCamera);
backfaceCamera.enabled = false;
backfaceCamera.clearFlags = CameraClearFlags.SolidColor;
backfaceCamera.backgroundColor = Color.white;
backfaceCamera.renderingPath = RenderingPath.Forward;
backfaceCamera.SetReplacementShader(backfaceShader, "RenderType");
backfaceCamera.targetTexture = GetBackfaceTexture();
}
backfaceCamera.Render();
}
private RenderTexture backfaceText;
private RenderTexture GetBackfaceTexture() {
if (backfaceText == null) {
backfaceText = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.RFloat);
backfaceText.filterMode = FilterMode.Point; //VERY IMPORTANT!
}
return backfaceText;
}
渲染背面深度的shader(来自kode80):
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unlit/BackfaceShader"
{
Properties
{
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Cull Front
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 position : POSITION;
float4 linearDepth : TEXCOORD0;
};
v2f vert(appdata_base v) {
v2f output;
output.position = UnityObjectToClipPos(v.vertex);
output.linearDepth = float4(0.0, 0.0, COMPUTE_DEPTH_01, 0.0);
return output;
}
float4 frag(v2f input) : COLOR
{
return float4(input.linearDepth.z, 0.0, 0.0, 0.0);
}
ENDCG
}
}
}
float camDepth = Linear01Depth(tex2Dlod(_CameraDepthTexture, float4(screenCoord.xy / 2 + 0.5, 0, 0)));
float backZ = tex2Dlod(_BackfaceTex, float4(screenCoord.xy / 2 + 0.5, 0, 0)).r;
if (pDepth > camDepth && pDepth < backZ) {
hitPixel = screenCoord.xy / 2 + 0.5;
debugCol = float3(hitPixel, 0);
return true;
}
如图
注意我在C#脚本中标注的IMPORTANT一行。少了这一行导致了一个非常难debug的bug。具体原因是相机的深度贴图是Point filter的,而自己创建的rendertexture是默认Bilinear filter的;如果不修改的话,我们用同一个坐标去采样会导致实际上是不同位置的采样进行相减。
要注意的是,这种获取物体厚度的办法并不万能。比如一个物体是只有单面的,此时厚度计算就会出问题(可以想想为什么),类似的,如果相机在一个物体内部(其实也相当于单面)也会出问题。
对于这些单面物体,如果是透明物体,可以设置为Transparent,不写入z缓冲,并且RenderType设置为非Opqaue,此时背面渲染shader就会忽视这个物体。
同时,此时光线有可能和物体的“背面“相交,但是毫无疑问我们只能获得物体“正面”的颜色信息。此时反射出现的内容依然是物体的正面,对于纯色物体这没什么问题,但是对于其他物体就会显得很weird了。
在Unity中实现屏幕空间反射Screen Space Reflection(2)的更多相关文章
- 在Unity中实现屏幕空间反射Screen Space Reflection(4)
第四部分讲一下如何在2D屏幕空间步进光线. http://casual-effects.blogspot.com/2014/08/screen-space-ray-tracing.html 中的代码感 ...
- 在Unity中实现屏幕空间反射Screen Space Reflection(1)
本篇文章我会介绍一下我自己在Unity中实现的SSR效果 出发点是理解SSR效果的原理,因此最终效果不是非常完美的(代码都是够用就行),但是从学习的角度来说足以学习到SSR中的核心算法. 如果对核心算 ...
- 在Unity中实现屏幕空间反射Screen Space Reflection(3)
本篇讲一下相交检测的优化.有两个措施. 线段相交检测 之前的检测都是检测光线的终点是否在物体内.我们可以尝试检测光线的线段是否与物体相交. 比如说有一个非常薄的物体,光线差不多垂直于它的表面.如果用普 ...
- 高级屏幕空间反射: Screen Space Reflection (SSSR)
SSSR进一步调优,对标寒霜级技术水平,实现方式为Direct3D 11+自主实现实时渲染引擎,方法为对比测试.实现已经有段时间了,还是简要更新下吧.以下画面中的SSSR效果全部采用1:4 resol ...
- 高级屏幕空间反射: Screen Space Reflection (SSR)
自从CE3首倡SSR以来,发展至今,其质量与当年早已不能同日而语.不仅强调超越性的质量,而且强调超越性的性能.乘着周末有空撸了撸,以下是增强型实时SSR结果图.与我原来的SSR原始实现相比,新的增强型 ...
- screen space reflection/soft alpha test/
http://www.crytek.com/cryengine/presentations/secrets-of-cryengine-3-graphics-technology 很多宝贝里面 不止题目 ...
- 在Unity中实现屏幕空间阴影(1)
接着上篇文章,我们实现了SSR效果. 其中的在屏幕空间进行光线追踪的方法是通用的.借此我们再实现一种屏幕空间的效果,即屏幕空间阴影. 文中的图片来自Catlike coding http://catl ...
- 在Unity中实现屏幕空间阴影(2)
参考文章: https://www.imgtec.com/blog/implementing-fast-ray-traced-soft-shadows-in-a-game-engine/ 完成的工程: ...
- 关于Unity中的屏幕适配
一.Game视图的屏幕分辨率可以先自定义添加,供以后选择,以下是手游经常用到的分辨率: 1.1136X640,iPhone5 2.1920X1080,横屏,主流游戏都是这个分辨率 3.1080X192 ...
随机推荐
- springmvc值传递
1.页面向后台传值 A.HttpServletRequest方式: package com.rong.controller; import javax.annotation.Resource; imp ...
- Web-JSP表单字符验证
代码后续补上
- linux之JDK安装
1.JDK安装 a.卸载JDK (1)卸载默认的JDK 用root用户登陆到系统,打开一个终端输入 # rpm -qa|grep gcj 显示内容其中包含下面两行信息 # java-1.4.2-gcj ...
- (转)elasticsearch5.2.2 压测配置
1.elasticsearch.yml # ---------------------------------- Cluster ----------------------------------- ...
- PHP 多维数组排序 array_multisort()
用PHP自带array_multisort函数排序 <?php $data = array(); $data[] = array('volume' => 67, 'edition' ...
- artdialog对话框 三种样式 网址:http://www.planeart.cn/demo/artDialog/_doc/labs.html
摇头效果 类似与wordpress登录失败后登录框可爱的左右晃动效果 // 2011-07-17 更新 artDialog.fn.shake = function (){ var style = th ...
- Java7 Fork-Join 框架:任务切分,并行处理
概要 现代的计算机已经向多CPU方向发展,即使是普通的PC,甚至现在的智能手机.多核处理器已被广泛应用.在未来,处理器的核心数将会发展的越来越多.虽然硬件上的多核CPU已经十分成熟,但是很多应用程序并 ...
- Contest 1
A:注意到模数是要求lcm的数的倍数,直接先取模就可以了.考场脑抽,对其质因数分解判了一下每个因子有没有,当然也行. #include<iostream> #include<cstd ...
- TortoiseSVN 和 VisualSVN Server 使用教程
TortoiseSVN 和 VisualSVN Server 使用教程 来源 https://blog.csdn.net/xgf415/article/details/75196360 目录: SVN ...
- 【刷题】BZOJ 4196 [Noi2015]软件包管理器
Description Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖( ...