如何利用Shader实现钻石ae渲染cpu利用率低效果

学习shader之前必须知道的东西之计算机图形学(一)渲染管线
精华热门加亮
引言
shader到底是干什么用的?shader的工作原理是什么?
其实当我们对这个问题还很懵懂的时候,就已经开始急不可耐的要四处搜寻有关shader的资料,恨不得立刻上手写一个出来。但看了一些资料甚至看了不少cg的语法之后,我们还是很迷茫,UNITY_MATRIX_MVP到底是个什么矩阵?它和v.vertex相乘出来的又是什么玩意?当这些问题困扰我们很久之后,我们才发现,原来我们是站在浮沙上筑高台,根基都没有打牢当然不可能盖得起高楼大厦了。
那根基是什么呢?大牛曰,计算机图形学。
shader中文名叫着色器,顾名思义,它的作用可以先简单理解为给屏幕上的物体画上颜色。而什么东西负责给屏幕上画颜色?当然是GPU,所以我们写shader的目的就是告诉GPU往屏幕哪里画、怎么画。说到这其实大家应该很明白了,如果我们连GPU的工作原理都不知道,何谈指挥它?
说到计算机图形学,包括我在内很多同学都非常害怕它,因为里面包含了各种艰深的理论、变换,大量的公式什么的。其实我们大可不必一开始就吓倒自己,先从基本概念开始,慢慢来,总有一天我们也会成为大牛~!
最后,这篇文章不算是原创,最多算是摘要+读后感,很多概念性文字都是我从书里搬过来后再加上自己的理解,算是和大家一起学习,有理解不当之处还请多多指教。
废话不多说,让我们来进入第一章的学习,GPU的渲染管线。
正文
所谓GPU的渲染管线,听起来好像很高深的样子,其实我们可以把它理解为一个流程,就是我们告诉GPU一堆数据,最后得出来一副二维图像,而这些数据就包括了”视点、三维物体、光源、照明模型、纹理”等元素。
在各种图形学的书中,渲染管线主要分为三个阶段:应用程序阶段、几何阶段、光栅阶段。
1,应用程序阶段。
这个阶段相对比较好理解,就比如我们在Unity里开发了一个游戏,其实很多底层的东西Unity都帮我们实现好了,例如碰撞检测、视锥剪裁等等,这个阶段主要是和CPU、内存打交道,在把该计算的都计算完以后,在这个阶段的末端,这些计算好的数据(顶点坐标、法向量、纹理坐标、纹理)就会通过数据总线传给图形硬件,作为我们进一步处理的源数据。
2,几何阶段。
主要负责顶点坐标变换、光照、裁剪、投影以及屏幕映射,改阶段基于GPU进行运算,在该阶段的末端得到了经过变换和投影之后的顶点坐标、颜色、以及纹理坐标。简而言之,几何阶段的主要工作就是“变换三维顶点坐标”和“光照计算”。
问题随之而来,为什么要变换顶点坐标?我是这么理解的,比如你有一个三维游戏场景,场景中的每个模型都可以用一个向量来确定它的位置,但如何让计算机根据这些坐标把模型正确的、有层次的画在屏幕上?这就是我们需要变换三维顶点坐标的原因,最终目的就是让GPU可以将这些三维数据绘制到二维屏幕上。
根据顶点坐标变换的先后顺序,主要有如下几个坐标空间:Object space,模型坐标空间;World space,世界坐标空间;Eye space,观察坐标空间;Clip and Project space,屏幕坐标空间。下图就是GPU的整个处理流程,深色区域就是顶点坐标空间的变换流程,大家了解一下即可,我们需要关注的是每个坐标空间的具体含义和坐标空间之间转换的方法。
726 || this.offsetHeight>700){if(this.offsetWidth/726 > this.offsetHeight/700){this.width=726;}else{this.height=700;}}" style="max-width:726max-height:700" title="点击查看原图" onclick="if(this.parentNode.tagName!='A') window.open('/forum/attachment/1305/thread/2_f3c9ec55e5d.png');" />
2.1,从object space到world space
object space有两层核心含义,第一,object space中的坐标值就是模型文件中的顶点值,这些值是在建立模型时得到的,例如一个.max文件,里面包含的数据就是object space的坐标。第二,object space的坐标与其他物体没有任何参照关系,这是object space和world space区分的关键。world space坐标的实际意义就有有一个坐标原点,物体跟坐标原点相比较才能知道自己的确切位置。例如在unity中,我们将一个模型导入到场景中以后,它的transform就是世界坐标。
2.2,从world space到eye space
所谓eye space,就是以摄像机为原点,由视线方向、视角和远近平面,共同组成的一个梯形体,如下图,称之为视锥(viewing frustum)。近平面,是梯形体较小的矩形面,也是靠近摄像机的平面,远平面就是梯形体较大的矩形,作为投影平面。在这个梯形体的内的数据是可见的,超出的部分会被视点去除,也叫视锥剪裁。
例如在游戏中的漫游功能,屏幕的内容随摄像机的移动而变化,这是因为GPU将物体的顶点坐标从world space转换到了eye space。
726 || this.offsetHeight>700){if(this.offsetWidth/726 > this.offsetHeight/700){this.width=726;}else{this.height=700;}}" style="max-width:726max-height:700" title="点击查看原图" onclick="if(this.parentNode.tagName!='A') window.open('/forum/attachment/1305/thread/2_f60b7fba0.png');" />
2.3,从eye space到project and clip space
eye space坐标转换到project and clip space坐标的过程其实就是一个投影、剪裁、映射的过程。因为在不规则的视锥体内剪裁是一件非常困难的事,所以前人们将剪裁安排到一个单位立方体中进行,这个立方体被称为规范立方体(CCV),CVV的近平面(对应视锥体的近平面)的x、y坐标对应屏幕像素坐标(左下角0、0),z代表画面像素深度。所以这个转换过程事实上由三步组成:
(1),用透视变换矩阵把顶点从视锥体变换到CVV中;
(2),在CVV内进行剪裁;
(3),屏幕映射:将经过前两步得到的坐标映射到屏幕坐标系上。
2.4,primitive assembly(图元装配)和triangle setup(三角形处理)
到目前为止我们得到了一堆顶点的数据,这一步就是根据这些顶点的原始连接关系还原出网格结构。网格由顶点和索引组成,这个阶段就是根据索引将顶点链接到一起,组成线、面单元,然后进行剪裁,如果一个三角形超出屏幕以外,例如两个顶点在屏幕内,一个顶点在屏幕外,这时我们在屏幕上看到的就是一个四边形,然后把这个四边形切成两个小的三角形。
现在我们得到了一堆在屏幕坐标上的三角形面片,这些面片是用于光栅化的。
3,光栅化阶段。
经过上面的步骤之后,我们得到了每个点的屏幕坐标值,和我们需要绘制的图元,但此时还有两个问题:
(1)屏幕坐标是浮点数,但像素是用整数来表示的,如何确定屏幕坐标值所对应的像素?
(2)如何根据已确定位置的点,在屏幕上画出线段或者三角形?
对于问题1,绘制的位置只能接近两指定端点间的实际线段位置,例如,一条线段的位置是(10.48, 20.51),转换为像素位置就是(10,21)。
问题2,涉及到具体的画线和填充算法,有兴趣的话可以研究。
这个过程结束后,顶点和图元已经对应到像素,之后的流程就是如何处理像素,即给像素赋予颜色值。
给像素赋予颜色的阶段称为Pixel Operation,是在更新帧缓存之前,执行最后一系列针对每个片段的操作,其目的是计算出每个像素的颜色值。在这个阶段,被遮挡的面通过一个被称为深度测试的过程消除。
pixel operation包含下面这些流程:
(1)消除遮挡面;
(2)Texture operation,纹理操作,根据像素的纹理坐标,查询对应的纹理值;
(3)Blending,通常称为alpha blending,根据目前已经画好的颜色,与正在计算的颜色的alpha值混合,形成新的颜色。
(4)Filtering,将正在计算的颜色经过某种滤镜后输出。
该阶段之后,像素的颜色值被写入帧缓存中。
码字好累....今天就先码到这里,有说的不对的地方还请大家多多指教!
共1条评分,
这样的文章好
要评论请先&或者&
好贴!不可多的的文章,阐述的很清楚。期待更新
顶~~~~~~~~
期待系列教程,感谢分享
收藏下......
学习了,我对于计算机图形学也是一知半解啊。
这样的文章好
此乃DX9的方式,有木有DX11的啊
很好的学习资料,
学习到了 期待更新2960人阅读
shader(8)
这次的目标是绘制一颗闪闪发光的钻石,追求效果是越接近真实越好。
先说说为此我这几天干了些什么。
1.看了stalendp blog里那篇《钻石效果》后头的参考文献
最有价值的就是ATI在2004年GDC上作的演讲,题目就叫Drawing a Diamond。但是由于只有ppt,所以很难重现工程深入学习。思路大概是预备了一张折射CubeMap,一张带模拟色散的反射CubeMap,最后再加上耀斑混合而成。
2.试验了一些简单的镜面反射
非实时的CubeMap反射最简单没什么好说的,需要注意的是要做到位置精确的镜面反射还是很难的,一不小心就会发现当物体离反射面很近时,尺寸位置都会严重错误。
像下图这样,球的镜像明显尺寸过大了。
实时获取环境CubeMap的反射也测试了下,没记错的话上图CubeMap的FaceSize设的是512,在我的一加手机上基本就跑不到正常帧了,FaceSize设成256帧率还可以接受,但画质就没法看了,总之实时反射是手机无法承受的。
这里我总结一点思想:
游戏画面表现本身就带有很多虚假的成分,因为全按真实的来计算性能根本无法承受,最简单的拿光照来说,不谈多次反射的环境光,单纯的一次平行光照实时计算都没法满足,都要预烘焙光照贴图,阴影也一样,真实的计算太费性能,一般也弄个假的完事。
所以我们会发现游戏画面和现实照片的差距总是一目了然。所以会出现很多专门掩饰的技巧。
如何抓住主要部分,在效果和性能间做出取舍,而又能尽量达到接近真实的效果,大概就是图形工程师们大多数时候在思考的事情了。
这个钻石效果的演示demo,明显要按固定环境的思路来,不需要考虑实时问题,像前面提到的那种尺寸扭曲的问题,基本也是可以忽略的,因为钻石形状类似小球,环境的轻微扭曲是能被轻易掩饰过去的。
3.到AssetStore搜索了下相关资源
发现免费的Gem Shaders就是Unity官方提供的,而且还为5.0专门更新过一次。我跑了下它的示例场景,感觉观感不是很满意。
主要是它提供的几个钻石模型形状我也不喜欢,于是自己到网上找了个大概是传说中“八星八箭”形状的模型,然后还是用这个shader试了下效果,结果发现还是很屌的。
我原本的想法是尽量用真实的光线折射、反射计算得到最终效果的。但是就算暂不考虑代码实现,不考虑实现后的性能如何,首先单纯就目前凭我有限的光学姿势能不能建立起真实准确的光学模型都很明显是成问题的。
所以这次我们还是以学习官方的示例为主,不要整天想自己搞什么大新闻了。
代码如下:
Shader &FX/Gem&
Properties {
_Color (&Color&, Color) = (1,1,1,1)
_ReflectionStrength (&Reflection Strength&, Range(0.0,2.0)) = 1.0
_EnvironmentLight (&Environment Light&, Range(0.0,2.0)) = 1.0
_Emission (&Emission&, Range(0.0,2.0)) = 0.0
[NoScaleOffset] _RefractTex (&Refraction Texture&, Cube) = && {}
SubShader {
&Queue& = &Transparent&
// First pass - here we render the backfaces of the diamonds. Since those diamonds are more-or-less
// convex objects, this is effectively rendering the inside of them.
Cull Front
ZWrite Off
#pragma vertex vert
#pragma fragment frag
#include &UnityCG.cginc&
struct v2f {
float4 pos : SV_POSITION;
float3 uv : TEXCOORD0;
v2f vert (float4 v : POSITION, float3 n : NORMAL)
o.pos = mul(UNITY_MATRIX_MVP, v);
// TexGen CubeReflect:
// reflect view direction along the normal, in view space.
float3 viewDir = normalize(ObjSpaceViewDir(v));
o.uv = -reflect(viewDir, n);
o.uv = mul(_Object2World, float4(o.uv,0));
samplerCUBE _RefractT
half _EnvironmentL
half4 frag (v2f i) : SV_Target
half3 refraction = texCUBE(_RefractTex, i.uv).rgb * _Color.
half4 reflection = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.uv);
reflection.rgb = DecodeHDR (reflection, unity_SpecCube0_HDR);
half3 multiplier = reflection.rgb * _EnvironmentLight + _E
return half4(refraction.rgb * multiplier.rgb, 1.0f);
// Second pass - here we render the front faces of the diamonds.
Blend One One
#pragma vertex vert
#pragma fragment frag
#include &UnityCG.cginc&
struct v2f {
float4 pos : SV_POSITION;
float3 uv : TEXCOORD0;
half fresnel : TEXCOORD1;
v2f vert (float4 v : POSITION, float3 n : NORMAL)
o.pos = mul(UNITY_MATRIX_MVP, v);
// TexGen CubeReflect:
// reflect view direction along the normal, in view space.
float3 viewDir = normalize(ObjSpaceViewDir(v));
o.uv = -reflect(viewDir, n);
o.uv = mul(_Object2World, float4(o.uv,0));
o.fresnel = 1.0 - saturate(dot(n,viewDir));
samplerCUBE _RefractT
half _ReflectionS
half _EnvironmentL
half4 frag (v2f i) : SV_Target
half3 refraction = texCUBE(_RefractTex, i.uv).rgb * _Color.
half4 reflection = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.uv);
reflection.rgb = DecodeHDR (reflection, unity_SpecCube0_HDR);
half3 reflection2 = reflection * _ReflectionStrength * i.
half3 multiplier = reflection.rgb * _EnvironmentLight + _E
return fixed4(reflection2 + refraction.rgb * multiplier, 1.0f);
// Shadow casting & depth texture support -- so that gems can
// cast shadows
UsePass &VertexLit/SHADOWCASTER&
下面我来分析其中的姿势点:
个人水平有限很多说法可能是错的,这里申明下先。
1.折射光的模拟
首先它的折射光并没有真实计算,而是给了这样一张CubeMap图,然后实际上是通过反射在计算。
2.pass设计
大致分两步,第一个pass是绘制钻石的背面,第二个当然就是前面了。注意第二个pass里的Blend One One,表示两个图层1:1混合。
考虑到钻石是个透明的物体,这样设计也是合情合理的。
3.光照模型设计
大致也是折射光、反射光、环境光、自发光各种叠加各种乘啦。反正因为白色是255,黑色是0,这里虽说算的是光,但实际处理的还是颜色,不管是加还是乘,都是越叠越亮,符合光线叠加的基本规律。至于它的计算公式是源自准确的光学原理,还是近似的模拟,我当然也不知道啦。
High Dynamic Range,不负责任的说下,大概就是假如0是黑色,1是白色,那么有些部分的颜色可以超过1,这样最终画面结果会呈现出一种亮处高闪耀的效果,具体请参考Unity官方文档说明。
5.菲涅尔效果
简单说就是因为光线射向玻璃时,一部分被反射,一部分直接射进去了,导致最终反射光线呈现一个视角越平行于入射面,反射效果越强,越垂直越弱的现象。
第二个pass计算时引入了这个因子。
所以一开始我说自己难以建立准确的模型,比如像这种细微而又真实存在的现象,仅凭一点粗浅的折射反射姿势而不去深入学习又怎么能考虑到呢?
这个shader里用了好多宏,在CGIncludes文件夹里的那些文件里都可以查到,这些我大致可以理解或者叫猜到怎么回事,但要真正弄清楚肯定得费一番功夫。
这里请允许猪哥偷个懒,随便说说装作懂了的样子,具体的计算细节就不一一展开了。
至此初阶段的shader学习计划就算完成了,由于我刻意赶了下进度,偷了些懒,比计划提前了一周。一分耕耘一分收获,shader这块水很深,想要有所造诣肯定得花更多的时间。但我出于全盘考虑目前并不想在这块继续投放精力。
下一阶段目光回到C#语言这块最基础的领域,继续修内力。去年买的《C#本质论》要收尾,一直没翻的《深入理解C#》也要看完。
谈语言难免会牵涉到设计模式,但也尽量避开。
周期定在五周吧。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:56505次
积分:1406
积分:1406
排名:千里之外
原创:84篇
评论:13条
(2)(3)(2)(5)(8)(4)(7)(3)(3)(9)(1)(1)(3)(8)(8)(12)(14)QQ群(带验证)
所有群已满,暂时关闭,每个月1-3号开放,群号会在这儿公布,到时候可以补空
高级素材推荐:Shader 和 GPU渲染的基础知识
我的图书馆
Shader 和 GPU渲染的基础知识
Shader?Shader描述了如何渲染物体的信息,包括Texture Setup、Material Property、Render State、Blend Setup、Pixel Shader、Vertex Shader、Render Target Setup等等。 &&Shader并不直接和Geometry相关联,因为对于同一个Geometry,有可能会用不同的Shader来渲染。Programmable Shader Architecture(可编程着色器架构)
HDR Rendering:&High Dynamic Range Rendering,&高动态范围渲染vs: vertex shader,&顶点处理/多边形处理的ps: pixel shader, &处理像素单位的阴影和处理相关纹理可编程Shader有担当顶点处理/多边形处理的[可编程顶点着色器]( Programmable Vertex Shader),和处理像素单位的阴影和处理相关纹理的[可编程像素着色器] (Programmable Pixel Shader)两种类型.在各自的着色器单元(Shader Unit)上,有着把各种各样的Shader程序来实现为3D图形的处理的结构。而且,作为用语,如果直接说[Programmable Shader],有指双方(VS和PS)的情况,或者是指这个整体的概念。
并且,由于这个Shader程序是软件,开发者可以自己制作独创的Shader程序,就可以在GPU上实现新的图形功能。
&关于Unified Shader,下图可以作为一个很好的说明。如果把Unified Shader翻译成“统一架构”的话,那么早期的就应该叫做“分离架构”。新架构的好处就是不会出现之前的vs很忙ps很闲,或者ps很忙vs很闲,能够最大限度的发挥所有着色器的能力,以相同着色器数量规模实现更高的性能。
工作原理:&DX11以后的渲染管线图(理论上期望获得的效果)&&&
TA的最新馆藏5025人阅读
Unity3D(20)
游戏(44)
好久没有写文章了,赶紧补几篇。最近研究了一个玻璃折射的效果(用在钻石上),虽然没有达到最满意的效果,还是先分享出来,待以后有更好的想法再补充。
先看效果吧:
这里面有两个效果,左边是unity的免费插件,右边的是我自己实现的,我将分别介绍这两个效果的实现方法。
一、知识补充:
钻石的图片:
两个shader都使用了CubeMap...
Shader &FX/Diamond&
Properties {
_Color (&Color&, Color) = (1,1,1,1)
_ReflectTex (&Reflection Texture&, Cube) = &dummy.jpg& {
TexGen CubeReflect
_RefractTex (&Refraction Texture&, Cube) = &dummy.jpg& {
TexGen CubeReflect
SubShader {
&Queue& = &Transparent&
// First pass - here we render the backfaces of the diamonds. Since those diamonds are more-or-less
// convex objects, this is effectively rendering the inside of them
Color (0,0,0,0)
Cull Front
ZWrite Off
SetTexture [_RefractTex] {
constantColor [_Color]
combine texture * constant, primary
SetTexture [_ReflectTex] {
combine previous, previous +- texture
// Second pass - here we render the front faces of the diamonds.
Fog { Color (0,0,0,0)}
Blend One One
SetTexture [_RefractTex] {
constantColor [_Color]
combine texture * constant
SetTexture [_ReflectTex] {
combine texture + previous, previous +- texture
// Older cards. Here we remove the bright specular highlight
SubShader {
// First pass - here we render the backfaces of the diamonds. Since those diamonds are more-or-less
// convex objects, this is effectively rendering the inside of them
Color (0,0,0,0)
Cull Front
SetTexture [_RefractTex] {
constantColor [_Color]
combine texture * constant, primary
// Second pass - here we render the front faces of the diamonds.
Fog { Color (0,0,0,0)}
ZWrite off
Blend One One
SetTexture [_RefractTex] {
constantColor [_Color]
combine texture * constant
// Ancient cards without cubemapping support
// We could use a 2D refletction texture, but the chances of getting one of these cards are slim, so we won't bother.
SubShader {
Color [_Color]
Shader &stalendp/myDiamondShader& {
Properties {
_Color (&Color&, Color) = (1,1,1,1)
_ReflectTex (&Reflection Texture&, Cube) = && { }
_RefractTex (&Refraction Texture&, Cube) = && { }
#include &UnityCG.cginc&
samplerCUBE _RefractT
samplerCUBE _ReflectT
struct v2f {
half4 pos:SV_POSITION;
half3 Reflect : TEXCOORD0;
half3 RefractR : TEXCOORD1;
half3 RefractG : TEXCOORD2;
half3 RefractB : TEXCOORD3;
half Ratio : TEXCOORD4;
v2f vert(appdata_full v) {
float EtaR = 0.65;
float EtaG = 0.67;
float EtaB = 0.69;
float FresnelPower = 5.0;
float F = ((1.0-EtaG) * (1.0-EtaG)) / ((1.0+EtaG) * (1.0+EtaG));
float3 i = -normalize(ObjSpaceViewDir(v.vertex));
float3 n = normalize(v.normal); // ??????
o.Ratio = F + (1.0 - F) * pow((1.0 - dot(-i, n)), FresnelPower) ;
o.RefractR = refract(i, n, EtaR);
o.RefractG = refract(i, n, EtaG);
o.RefractB = refract(i, n, EtaB);
o.Reflect = reflect(i, n);
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
fixed4 frag(v2f i) : COLOR0 {
float3 refractColor, reflectC
refractColor.r = float3(texCUBE(_RefractTex, i.RefractR)).r;
refractColor.g = float3(texCUBE(_RefractTex, i.RefractG)).g;
refractColor.b = float3(texCUBE(_RefractTex, i.RefractB)).b;
reflectColor = float3(texCUBE(_ReflectTex, i.Reflect));
2*_Color * pow(fixed4(lerp( _Color * refractColor * 5, reflectColor * 2, i.Ratio), 1), 1.8);
SubShader {
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
FallBack Off
关键词: diamond、crystal、glass、refraction、cubeMap、钻石、水晶、玻璃、折射
参考资料:
1. CubeMap技术相关:
2.[GPU Gems]Environment Mapping Techniques:
3.《OpenGL Shading Language 3rd edition》14.1 Refraction :&
4.[GPU Gems]Generic Refraction Simulation:
5. 某个大学的ppt:
6. Fresnel原理:
7.&AMD关于diamond算法的文章:
8. CG Shaders - Cubemap Refraction:&
9. 水滴折射效果:
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:433033次
积分:4899
积分:4899
排名:第5399名
原创:90篇
评论:197条
(2)(1)(2)(1)(2)(1)(1)(1)(2)(3)(1)(1)(1)(2)(3)(1)(1)(2)(1)(2)(2)(2)(2)(2)(9)(4)(9)(2)(2)(7)(4)(3)(7)(1)(1)(1)(1)(1)

我要回帖

更多关于 钻石材质渲染教程 的文章

 

随机推荐