《GPU GEMS》16.次表面散射的实时近似

次表面散射的通俗理解,即对于某些半透明物体,如蜡烛,皮肤,牛奶等。光线进入物体,在物体内部发生散射现象,最终从入射点不同的位置射出。
这使得对于物体表面的每个像素着色来讲,不止受到该点入射光的影响,还会收到表面其余散射出来的光线的影响。这让次表面散射与漫反射区别开来。

1.简单的散射近似
简单的散射近似使用了环绕光照(wrap lighting)
Lambert(理想的漫反射模型)在光线与表面法线方向垂直时,值为0
环绕光照修改Lambert,使得Lambert的值域在缩小,a=环绕光照值
效果:使得物体原本应该黑暗的地方拥有一定的光照,即相比Lambert模型,阴影部分后退。
环绕光照函数
y=(x+wrap)/(1+wrap)=1-(1-x)/(1+wrap),x<=1.0

常规漫反射计算:
float diffuse=max(0,dot(L,N));
环绕光照漫反射计算:
float wrap_diffuse=max(0,(dot(L,N)+wrap)/(1+wrap));
L 光照方向
N 表面法线

颜色漂移

Paste_Image.png

根据环绕光照公式,生成皮肤着色查找表,然后根据皮肤查找表进行着色。
思路:提高暗部亮度,阴影过渡处颜色替换。
使用查找表是为了加速计算。

// Generate 2D lookup table for skin shading
//input:p(x,y)
//output:color(r,g,b,a)
float4 GenerateSkinLUT(float2 P : POSITION) : COLOR
{
  //wrap环绕系数
  float wrap = 0.2;
  //散射宽度
  float scatterWidth = 0.3;
  //散射颜色
  float4 scatterColor = float4(0.15, 0.0, 0.0, 1.0);
  //光泽度
  float shininess = 40.0;
  //NdotL:表面法线Normal与光照方向LightDir的点积
  //NdotH:表面法线Normal与半角向量HalfVec的点积
  float NdotL = P.x * 2 - 1;  // remap from [0, 1] to [-1, 1]
  float NdotH = P.y * 2 - 1;
  //进行环绕光照方程计算后的值
  float NdotL_wrap = (NdotL + wrap) / (1 + wrap); // wrap lighting
  //使用环绕光照的漫反射
   float diffuse = max(NdotL_wrap, 0.0);

  // add color tint at transition from light to dark
  //smoothstep(start,end,t);
  //t<start,return 0
  //t>end,return 1
   float scatter = smoothstep(0.0, scatterWidth, NdotL_wrap) *
                    smoothstep(scatterWidth * 2.0, scatterWidth,
                               NdotL_wrap);
  //计算高光Blinn-Phong模型
  float specular = pow(NdotH, shininess);
  if (NdotL_wrap <= 0) specular = 0;
  float4 C;
  C.rgb = diffuse + scatter * scatterColor;
  C.a = specular;
  return C;
}

// Shade skin using lookup table

   half3 ShadeSkin(sampler2D skinLUT,
                half3 N,
                half3 L,
                half3 H,
                half3 diffuseColor,
                half3 specularColor) : COLOR
{
  half2 s;
  s.x = dot(N, L);
  s.y = dot(N, H);
  //s*0.5+0.5,将点积的值域从[-1,1]重新映射到[0,1]
  half4 light = tex2D(skinLUT, s * 0.5 + 0.5);
  return diffuseColor * light.rgb + specularColor * light.a;
}

方法解析:
第一个函数是生成2D查找表的方程,根据第二个函数的输入,最终应该是生成一张texture2D的贴图,贴图的每个像素点记录了我们需要的颜色信息。
第二个函数即根据点积结果查找表中的颜色值,和漫反射颜色进行计算叠加‘。
实际操作上应该是提前烘焙好皮肤2D查找表贴图,然后在shader里直接使用该贴图读取颜色值和diffuse做运算。
理论很简单,甚至没有什么技术含量,效果根据上图来讲也比较一般。

2.用深度映射模拟吸收
比起上一个的改进是,这个技术模拟光在物体内传播的吸收过程,这使得我们需要计算光在物体中前进的距离,因为距离越长,被物体吸收的光线也就越多。表现就是,对于一个半透明的物体,光源位于物体背后时,我们在物体前方可以看到物体部分区域被照亮,照亮程度与厚度(光在物体内部传播的距离)有关。这个技术不考虑光进入物体内部产生的折射现象,同时只适用于凸面物体


距离的计算思路是和阴影映射的方法相似。关于阴影映射参考:
http://blog.csdn.net/xiaoge132/article/details/51458489
以光源为视点进行烘焙(将摄像机的位置移动到光源位置进行烘焙),获得表面上的点与光源之间的距离,存储到一张texture中。 using standard projective texture mapping将图像投射到场景中。然后在渲染pass中,给需要渲染的点,从texture中我们获得它到光源的距离,同时获得他背后的点,即光的入射点的距离,两者距离相减即得到光在物体的传播距离。然后根据这个距离,按照自己预先设定的衰减查找表,进行着色。

The Vertex Program for the Depth Pass
//深度Pass程序
//顶点着色器的输入结构体
struct a2v {
  float4 pos    : POSITION;
  float3 normal : NORMAL;
};
//顶点着色器的输出,即片段着色器的输入
struct v2f {
  float4 hpos : POSITION;
  float  dist : TEXCOORD0; // distance from light
};
//顶点着色器程序
//输入:a2v结构体,模型视图投影矩阵,模型视图矩阵,grow值
v2f main(a2v IN,
         uniform float4x4 modelViewProj,
         uniform float4x4 modelView,
         uniform float    grow)
{
  v2f OUT;
  float4 P = IN.pos;
  //沿法线方向缩放顶点
  P.xyz += IN.normal * grow;  // scale vertex along normal
  //矩阵变换,返回一个顶点
  OUT.hpos = mul(modelViewProj, P);
  //距离计算,返回一个距离的浮点值
  OUT.dist = length(mul(modelView, IN.pos));
  return OUT;
}
The Fragment Program for the Depth Pass

float4 main(float dist : TEX0) : COLOR
{
  return dist;  // return distance
}
The Fragment Program Function for Calculating Penetration Depth Using Depth Map

// Given a point in object space, lookup into depth textures

   // returns depth

   float trace(float3 P,
            uniform float4x4  lightTexMatrix, // to light texture space
            
   uniform float4x4  lightMatrix,    // to light space
            
   uniform sampler2D lightDepthTex,
            )
{
  // transform point into light texture space
  
   float4 texCoord = mul(lightTexMatrix, float4(P, 1.0));

  // get distance from light at entry point
  
   float d_i = tex2Dproj(lightDepthTex, texCoord.xyw);

  // transform position to light space
  
   float4 Plight = mul(lightMatrix, float4(P, 1.0));

  // distance of this pixel from light (exit)
  
   float d_o = length(Plight);

  // calculate depth
  
   float s = d_o - d_i;
  return s;
}

技术缺陷是没有考虑到光的漫反射,光源在物体背后时会显示背面细节

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 我们都知道,一个三维场景的画面的好坏,百分之四十取决于模型,百分之六十取决于贴图,可见贴图在画面中所占的重要性。在...
    自由的天空阅读 12,327评论 0 12
  • 一、Surface Output (表面着色器的标准输出结构)Surface Shader的标准输出结构-第一要素...
    CarlDonitz阅读 889评论 0 1
  • Unity3D在5.0引入了PBS(Physically-Based Shading)特性,这一光照模型取代了La...
    xClouder阅读 8,439评论 1 22
  • 在讲Unity光照模型之前,先介绍图形学中的两个基础光照模型原理,将会更利于我们理解和使用Unity中的光照模型。...
    Unity云中客阅读 3,556评论 1 6
  • 我收获了什么,20年的青春岁月。 今天是自行车在林州市开始的第一天。我觉得一切都又回到了起跑线,40岁的吃螃蟹。 ...
    105d45b91b02阅读 211评论 0 0