【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 2601|回复: 2

[转帖]Shaderey――非真实渲染

[复制链接]
发表于 2008-9-1 00:39:27 | 显示全部楼层 |阅读模式

本文版权归原作者所有,仅供个人学习使用,请勿转载,勿用于任何商业用途。
由于本人水平有限,难免出错,不清楚的地方请大家以原著为准。欢迎大家和我多多交流。
作者:Aras Pranckevicius
翻译:clayman
Blog:
http://blog.csdn.net/soilwork
clayman_joe@yahoo.com.cn

本文描述了以非真实渲染(none-photorealistic rendering)风格,对户外场景进行着色的技术。在2003年秋天的Beyond3D/ATI shader compititon中,Shaderey程序最先使用了这些技术来进行渲染。在Shaderey的户外场景中,包含了地形,云,树木,房屋,天空顶,以及湖水,如图所示:

确切的说,这里使用的NPR技术都是在图片空间(image space)进行的操作,它依赖于场景中两张重要的图片:一张包含了颜色信息,一张包含法线和深度信息。处理过程分为两部分:

渲染: 把场景渲染到颜色和法线/深度目标中。

后期处理: 在图片空间进行一系列过滤操作,获得最终的非真实效果。

后期处理包括:HSV空间下的颜色扭曲,屏幕空间中简单“阴影线(hatching)”的渲染,以及在法线/深度不连续处的轮廓线绘制。我们将在后面详细讨论这些过滤操作。首先,先来看看Shaderey的场景渲染方式。

场景渲染

场景中所有的树木和房屋都经过了可视体裁剪(frustumculled)。地形是一张512 x 512的高度图,但分为若干尺寸固定的(32 x 32)小块(chunk)。所有通过视见体裁剪的地形小块都没有进行任何形式的LOD。整个场景使用了一张1024 x 1024的阴影帖图。房屋和树木都将产生阴影,并且投影到地面上。场景中的树木和木屋投射阴影,而地形接收这些影子。我们使用pick-nearest采样器,对阴影贴图进行四次有偏移的采样,然后再shader中对这些值进行均值采样,以提高影子边界上的质量。阴影贴图并不需要覆盖整个地形的大小,在我们的实现中,它将随观察者的位置移动,以保证观察者前方总是有正确的阴影。

为了模拟湖面的简单反射效果,可以把摄像机反转到水面之下,把场景渲染为一张较小的平面反射贴图。我们把这张阴影贴图投影到水面上,另外使用两张卷动的EMBM风格的凹凸贴图来模拟波纹。为了减少几何数据,渲染到反射贴图中的地形将使用较低的LOD层次。对所有物体来说,大气光照散射效果都是在顶点级别计算的。

除了把颜色渲染到后备缓冲之外,还需要把场景中物体的法线和深度渲染到一张和屏幕大小相同的A8R8B8G8纹理中。世界坐标下的法线信息保存在RGB通道中,深度值的导数保存在alpha通道中。

下面是在vertex shader中,使用HLSL正确计算法线和深度值倒数的代码:

//output normal in RGB, sort-of-depth in A, p – final ( clip space) position, n—world space normal

static inline float4 gNormalZ( float4 p, float3 n)

{

float4 o;

o.xyz = n * 0.5 + 0.5; // in to 0….1 range

o.w = 100.0 / ( p.w + 100 ); // kind-of-depth

}

如果支持DirectX 9中的Multiple Render TargetMRT),可以在渲染场景颜色的同时,渲染法线和深度。如果不支持MRT,则需要分两次渲染(译注:从demo来看,使用MRT将会严重影响渲染质量,应该是由于MRT不支持多重采样造成的)。当把地形渲染到法线/深度纹理中时,需要使用<< Non-Photorealistic Rendering with Pixel and Vertex Shader>>中所描述的方法,在pixel shader中对阴影贴图进行采样,对阴影中的像素来说,需要对插值之后深度值取反(译注:Non-Photo原文中是对法线值取反)。这样做的原因在后面描述后期处理的部分会讲解。

图片后期处理

目前已经把场景渲染为颜色和法线/深度图片了,接下来就可以对这些图片进行一系列处理了,包括把颜色转到HSV颜色空间下进行风格化处理,绘制边缘轮廓线,实现阴影线。

颜色失真

图片处理的第一步是进行颜色失真,获得风格化的样式。

1.降低采样率,把图片缩为一张521x512的纹理。

2.把颜色从RGB空间转换到HSV空间,并且量化(quantize)颜色值。颜色空间的转换将通过对一张体积材质的查找来实现。把原像素的RGB值作为立方纹理坐标。立方纹理中的像素为HSV颜色空间。这里我们将使用一张32x32x32的纹理,并且不进行任何过滤,所以颜色转换的同时将会量化颜色值。

3.使用2D偏移纹理,对同一纹理中当前像素的两个偏移位置进行采样。用来访问偏移纹理的纹理坐标由程序控制,它们将和观察者的位置有关(观察点的yaw值将在水平方向影响偏移,pitch值在垂直方向影响)。这些额外的采样颜色也必须转换到HSV空间。

4.替换图片中的颜色。目前我们有2个额外的偏移采样。首先,我们检察两个偏移值之间差分的差值,如果小于某个限制,就什么也不做。如果它们之间的差别足够大,则输出SV通道的均值,保留中心原像素的H值。这个方法能高效的在颜色区域边缘替换原像素的饱和度。

 楼主| 发表于 2008-9-1 00:39:55 | 显示全部楼层

5.再使用一张立方纹理把颜色转换回HSV空间。

2~5步的pixel shader代码如下,需要pixel shader 2.0的支持。

struct PS_INPUT

{

float2 uv[2] : TEXCOORD0; //base uv,displace uv

};

float4 psMain( PS_INTPUT i) : COLOR

{

//sample rgb,convert into hsv

half base = tex2D( smpBase, i.uv[0] ).rgb;

base = tex3D( smpRGB2HSV, base ).rgb;

//get 2 displaced sample locations

half2 bleedB = tex2D ( smpBleedB, i.uv[1] ).rg * 2 -1;

half2 bleedC = tex2D ( smpBleedC, i.uv[1] ).rg * 2 -1;

float2 uvB = i.uv[0] + bleedB * (8.0/512);

float2 uvC = i.uv[0] + bleedC * (-7.0/512);

//sample base at displaced locations ,convert to hsv

half3 baseB = tex2D( smpBase,uvB).rgb;

baseB = tex3D( smpRGB2HSV,baseB);

half3 baseC = tex2D( smpBase, uvC).rgb;

baseC = tex3D( smpRGB2HSV,baseC);

half3 bleed = baseB * 0.5 + baseC * 0.5;

//final color is base if differences in hsv values are smller than tresholds

//else average of displace values

half3 diff = abs(base - baseC) - half( 1/8.0,1/3.0,1/3.0)

half3 final = all( diff < float3 ( 0,0,0) ? base : bleed;

//leave original hue channel

final.r = base.r;

//convert back to rgb

return tex3D ( smpHSV2RGB),final);

}

边缘检测和轮廓线

为了获得NPR风格的样式,必须在图片上渲染出深色的轮廓线和阴影线,表现出场景的着色效果。在Shaderey中,我们将同时绘制边缘轮廓线和阴影线。这里需要使用之前计算的法线/深度图来计算边缘,用光线和法线的点积来计算那些区域需要绘制阴影线。阴影线是一张简单的纹理。在这一步处理中,边缘和轮廓线都是白色。最终合成时,进行反色处理,轮廓线变为纯黑色,轮廓线颜色根据场景的着色进行衰减。

以下是绘制轮廓线和阴影线的pixel shader代码:

half4 psMain ( float2 uv[3]:TEXCOORD): COLOR

{

//sample center and 2 neightbours

half4 cbase = tex2D( smpBase, i.uv[0]);

half4 cb1 = tex2D(smpBase, i.uv[1]);

half4 cb3 = tex2D(smpBase, i.uv[2]);

//normal into -1..1 range

half3 nbase = cbase.xyz * 2 -1;

half3 nb1 = cb1.xyz * 2 - 1;

half3 nb3 = cb3.xyz * 2 - 1;

//edges from normals

half2 ndiff;

ndiff.x = dot( nbase,nb1);

ndiff.y = dot( nbase,nb3);

ndiff -= 0.6;

ndiff = ndiff > half2(0,0) ? half2(0,0):half2(1,1);

half ndiff1 = ndiff.x + ndiff.y;

//edges from z

float2 zdiff;

zdiff.x = cbase.a - cd1.a;

zdiff.y = cbase.a - cb3.a;

adiff = abs(zdiff) - 0.02;

zdiff = zdiff > half2(0,0) ? half2(1,1) : half2(0,0);

//sampler hatch

half4 chatch = tex2D( smpHatch, i.uv[0]);

//dot normal with light

half dotNL = dot( nbase, vLightDir);

//hatch blend factor

half factor = saturate( (1.0 - 0.9 - dotNL) * 2);

chatch *= factor;

return chatch + ndiff1 + dot(zdiff,half2(1,1));

}

 楼主| 发表于 2008-9-1 00:40:08 | 显示全部楼层

最终合成

在处理完了两张图片之后,把失真之后的颜色与反转之后的边缘/轮廓线进行调制,合成出最终图像。

点击这里下载完成程序和代码。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这是《ShaderX3》里的一篇文章,第一次看到这个demo时很是惊奇,嗯嗯,不是所有游戏都要做的像DOOM那样才能吸引人,非真实渲染也是很有意思的风格。文章倒是老早就写完了,最近有朋友正在研究NPR,所以整理出来,希望能有帮助。

ps:造物弄人啊,这是扭曲的一周! 明天终于要回家了,怀念昆明的太阳^o^~~~~~~~~

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2025-2-6 06:53

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表