Clayman的专栏

It's all about XNA & GPU Programming

用户操作
[即时聊天] [发私信] [加为好友]
孙凌峰ID:soilwork
192047次访问,排名382好友0人,关注者12
soilwork的文章
原创 85 篇
翻译 15 篇
转载 0 篇
评论 329 篇
clayman的公告
嘿嘿 ^o^....
最近评论
jym5596337:我也不知道我怎么就走到了你的路上来了呵呵...
太晕了,我们专业就学的C#... 那我就凑合着用它学习MDX喽,但到了2008你的这个时期,感觉形式有点尴尬,以前的人说 在中国搞软件是 前有微软,后有盗版。
现在是 前有XNA后有C++ ... MDX 学习资料太太太难找了。。太太太少了。 师兄给介绍下你学历路途中的资料目录咯。。。我好找来学习咯。。 感谢哦感谢。。
jym5596337:好象没有继续哇 ...
shapin:ATI的网站有个支持HLSL语法高亮的vs插件,可以支持其他版本,只要修改相应的那个注册表就行
flip:To linxv :
貼圖座標有用投影嗎?
linxv:我用了你上面的基本技术,确实实现了纹理扰动,非常感谢!但是有一个问题是那张被扰动的图会跟着摄象机动,不知道是什么原因!
文章分类
收藏
    相册
    blogs
    David Weller
    nVidia Developer blog
    Rico Mariani
    Shawn Hargreaves
    XNA Team blog
    XNA资源
    XNA Creators Club
    ZBuffer
    Ziggyware XNA Resources
    中国XNA开发网
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 Shaderey――非真实渲染收藏

    新一篇: 《The Complete Effect and HLSL Guide》翻译连载(十二) | 旧一篇: 值类型属性??

    Shaderey――非真实渲染

    本文版权归原作者所有,仅供个人学习使用,请勿转载,勿用于任何商业用途。
    由于本人水平有限,难免出错,不清楚的地方请大家以原著为准。欢迎大家和我多多交流。
    作者: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值。这个方法能高效的在颜色区域边缘替换原像素的饱和度。

    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));

    }

     

    最终合成

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

     

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

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

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

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

    发表于 @ 2007年01月11日 20:50:00|评论(loading...)|编辑

    新一篇: 《The Complete Effect and HLSL Guide》翻译连载(十二) | 旧一篇: 值类型属性??

    评论

    #fannyfish 发表于2007-01-12 17:59:56  IP:
    想起了 赛尔达传说——风之杖
    #htqx 发表于2007-01-30 14:02:57  IP: 218.15.2.*
    效率如何,可以运用到实际游戏中去么?
    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © clayman