• 《OgreBeginner'sGuidede》第七章翻译(原)


    以下内容仅供学习使用,敢拿这么差的翻译用的,额,我估计还没出生,所以就不担心什么了,如有不妥之处,欢迎联系,敝人定当痛改!

    第一次做翻译,断断续续很不顺利,这也大程度上反映了为什么我去六级考场刷分为什么一次比一次创新底的原因,到后边都有些从心所欲了,所以难免有很多纰漏,千万还请见谅

    Zephyroal 201105

    Ogre3D材质系统初窥

    长话短说,想做出美轮美奂的场景么1?让我们开始了解材质的原理和使用,记住,认真实践,神奇的大门正向你打开!

    在这一章中,我们将会学习:

    ·自定义材质

    ·应用材质到手动创建的四边形上

    ·理解渲染管线的工作原理

    ·Shader

    创建一个四边形

    相信你通过前几章的学习之后你已经懂得了如何导入3维的Mesh文件了吧?!那2D岂不是小菜一碟?对,让我们开始吧

    开工:

    建立一个空工程,在createScene()函数中加入如下代码

    1. Begin with creating the manual object:

    Ogre::ManualObject* manual = mSceneMgr->createManualObject("Quad");

    manual->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST);

    首先创建一个ManualObject

    2. Create four points for our quad:

    manual->position(5.0, 0.0, 0.0);

    manual->textureCoord(0,1);

    manual->position(-5.0, 10.0, 0.0);

    manual->textureCoord(1,0);

    manual->position(-5.0, 0.0, 0.0);

    manual->textureCoord(1,1);

    manual->position(5.0, 10.0, 0.0);

    manual->textureCoord(0,0);

    加入相应的顶点信息,pos,uv

    3. Use indices to describe the quad:

    manual->index(0);

    manual->index(1);

    manual->index(2);

    manual->index(0);

    manual->index(3);

    manual->index(1);

    填充相应的索引,不熟悉的话简单地解释一句,它就是一个数组的下标所集合成的另一个数组,为什么这么做?想像一下如果第一个数组数据很大,而且得不断重用就明白了

    4. 完成ManualObject数据的输入:

    manual->end();

    manual->convertToMesh("Quad");

    又回到开始讲的了,在dx8后管线里的2d其实早已不复存在,说是2dManualObject,饶了一圈它其实还是一个mesh,

    这里闲话几句:直接跳入源码,

    Building one-off geometry objects manually usually requires getting down and dirty with the vertex buffer and vertex declaration API, which some people find a steep learning curve. This class gives you a simpler interface specifically for the purpose of building a 3D object simply and quickly. Note that if you intend to instance your object you will still need to become familiar with the Mesh class.

    之于Ogre,老外真的写得很用心,如果熟悉DX的话,对这种创建方式你应该感到一定的熟悉,可以看到在"ManualObject"工厂里注册生成一个Quad的object,然后在每次begin后接收,填入vertex。谨记,搞懂了原理才能随性所欲地去创造,

    5.创建实体,并附在场景节点之上

    Ogre::Entity * ent = mSceneMgr->createEntity("Quad");

    Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()- >createChildSceneNode("Node1");

    node->attachObject(ent);

    有了mesh,老套路,创建Entity和SceneNode,挂接,如果还不清楚老实翻回前几章

    6. 没错的话结果应该如下:

    clip_image002

    创建我们自己的材质:

    如果只是一味地将一切渲染成白色是不是很无趣呢?让我们开始创建属于自己的第一种材质。

    行动:

    现在,我们将利用之前的白色材质改成一个新的材质

    1. Change the material name in the application from BaseWhiteNoLighting to MyMaterial1:

    manual->begin("MyMaterial1", RenderOperation::OT_TRIANGLE_LIST);

    将之前创建的manualObject的材质改为MyMaterial1

    2.

    在OgreSDK的media\ materials\scripts下创建一个名为Ogre3DBeginnersGuide.material的文件

    3. 写入如下代码:

    material MyMaterial1

    {

    technique

    {

    passMaterials with Ogre 3D [ 132 ]

    {

    texture_unit

    {

    texture leaf.png

    }

    }

    }

    }

    4.

    输入如上的材质脚本后,编译,你会看到如下结果

    clip_image004

    刚才所发生的:

    刚从我们所做的事就是创建了Ogre自己的材质文件(.material),当然,为确保ogre能正确找到指定的材质文件,我们需要在resourc.cfg中定义相应的路径,熟悉ogre之后,你也可以在ResourceManager中直接引入

    Ogre材质系统至少在传统的渲染体系中表现出了极大的灵活性,正如上面显示的,在程序中我们只要指定下材质名字,ogre就能完成一切,让我们接下来具体探索有意思的Material系统

    材质

    首先每一个材质的定义都由一个关键字“”开始,以花括号{}表示头尾,语法类似我们的平时编程习惯,而每个材质中可以由多个technique构成,technique在这里可以理解为,由于大家的硬件配置各不相同,一种材质为了达到不同的效果等级而预定义的多种“方法”,注意,这里的technique可是要分先后的,通常最炫的也是要求最高的technique放在压轴。在指定的一个technique中,又可以定义多个pass,一个pass就是将当前材质物体的几何信息渲染的一个批次,通常,pass只有一个,(但如果你想要你的程序能有让人眼前一亮的实力,那么,努力去做一个硬件杀手吧,别害怕,多尝试,走极限,你会发现自己的功力会开始飞涨~)如同套娃娃一样,pass中还定义有具体的texture unit,这个很容易理解了吧,贴图,传统意义上的材质就是它加上顶点色和些许渲染状态设置了,那么Ogre这是在将问题复杂化了么?欲见分晓,客观还请耐心往下看

    接下来我们开始创建一个新的材质

    注:材质纹理坐标:

    在上一章我们讨论过纹理有超过0~1范围的情况,现在,就让我们看看具体会产生什么样的效果

    1. 将前面创建四边形的代码略做修改:

    manual->position(5.0, 0.0, 0.0);

    manual->textureCoord(0,2);

    manual->position(-5.0, 10.0, 0.0);

    manual->textureCoord(2,0);

    manual->position(-5.0, 0.0, 0.0);

    manual->textureCoord(2,2);

    manual->position(5.0, 10.0, 0.0);

    manual->textureCoord(0,0);

    2. 编译,我们可以看到图像显示了四次

    clip_image006

    刚才所发生的:

    clip_image008

    我们仅仅将quad的纹理坐标定义为了0~2,我们知道UV坐标系的定义是由0~1,这就意味着Ogre在渲染的时候就必须采取策略模式来对应这种情况了,默认情况下这个模式是wrap。在这种模式下当uv坐标大于1时,纹理坐标会简单的重复,详见上图,简单的说,就是把uv小数点前的数字忽略。这里就不多废话了,看下DX或OpenGL的具体实现会有很好理解的

    译者注:

    (推荐一篇小手册,可以去看下,加深理解:

    http://www.gesoftfactory.com/developer/Textures.htm

    好,现在我们已经看到了默认的纹理寻址模式的作用,但它并未体现出这种模式的优势所在,接下来我们用另一张纹理来Show it up

    在接下里这个例子中,我们可以发现wrap的作用

    1.创建一个类似的材质脚本:

    material MyMaterial3

    {

    technique

    {

    pass

    {

    texture_unit

    {

    texture terr_rock6.jpg

    }

    }

    }

    }

    2. 在C++中调用新的材质:

    manual->begin("MyMaterial3", RenderOperation::OT_TRIANGLE_LIST)

    3.如果看过原始纹理,会发现很神奇吧?

    clip_image010

    刚才所发生的:

    简单的说,这就是游戏里常用的“无缝纹理”,具体实现咱程序可以不用多管,具体就是ps的修修剪剪罢了

    接下来我们再尝试一下另一种纹理寻址模式——clamping

    1. 创建材质:

    material MyMaterial4

    {

    technique

    {

    pass

    {

    texture_unit

    {

    texture terr_rock6.jpg

    }

    }

    }

    }

    2.

    在materil文件中我们将tex_addres_mode设为了clamp

    tex_address_mode clamp

    3. 将 MyMaterial3 改为 MyMaterial4:

    manual->begin("MyMaterial4", RenderOperation::OT_TRIANGLE_LIST);

    4. 编译后如下

    clip_image012

    刚才所发生的:

    在clamp模式中,我们使用了纹理边框的像素点颜色扩充到超出uv正常范围的区域,这也就是有些纹理我们必须要求美工将纹理图片的周围一圈设为透明的原因,如比较有用的ProjectedTex,有兴趣的可以去看下wiki中的中级教程

    注,作者在接下来对Mirror和Boder模式做了介绍,一来不常用,二来强烈感觉作者有打流水帐放水的嫌疑,译者坚决不与其同流,故暂时不作翻译跳过,有兴趣的可以拿原文过来自己读读,嘿嘿,因为,甜点结束,开始上正餐了

    纹理坐标滚动:

    纹理寻址只是对纹理的一种基本操作,现在,我们将会去使用一个非常有用的纹理操作

    与前面基本相同

    1 改变纹理坐标从2到0.2:

    manual->begin("MyMaterial8", RenderOperation::OT_TRIANGLE_LIST);

    manual->position(5.0, 0.0, 0.0);

    manual->textureCoord(0.0,0.2);

    manual->position(-5.0, 10.0, 0.0);

    manual->textureCoord(0.2,0.0);

    manual->position(-5.0, 0.0, 0.0);

    manual->textureCoord(0.2,0.2);

    manual->position(5.0, 10.0, 0.0);

    manual->textureCoord(0.0,0.0);

    2. 在新的材质脚本MyMaterial8中改动texture terr_rock6.jpg:

    material MyMaterial8

    {

    technique

    {

    pass

    {

    texture_unit

    {

    texture terr_rock6.jpg

    }

    }

    }

    }

    3. 编译结果如下:

    clip_image014

    刚才所发生的:

    重复了前面的工作,唯一不同的是这回我们将纹理坐标设置为0.2,也就是只显示图片的1/5,作者同时善意提醒,如果到现在还不是很清楚的话,翻回去再读一遍

    接下来尝试-滚动纹理:

    1.加入如下代码:

    scroll 0.8 0.8

    2. 可以看到,显示的是纹理的另一部分:

    clip_image016

    在材质脚本内填入scroll 的参数后,可以发现显示结果不同了

    刚才所发生的:

    scroll属性改变给定纹理坐标的偏移量,通过下面图表,可以简单地认为,scroll是一个增加量

    clip_image018

    利用这个属性,将来可以用于改变给定mesh中uv坐标

    动态地滚动纹理:

    scroll最大的作用还是加上动态效果,不知读者有没有玩过天龙八部,里边的瀑布场景不错,就是用这个实现的,而且效能廉价,心动了?那就赶快去试试

    实践:

    1. 加入一个新材质,并改为scroll_anim属性:

    scroll_anim 0.01 0.01

    2. 改变C++中的引用材质

    3. 编译运行,仔细观察可以.

    刚才所发生的:

    scroll_anim与scroll属性有很大的相似,只不过它是每秒钟的uv方向上的偏移量,具体的纹理操作还有很多,具体参见如下:

    http://www.ogre3d.org/docs/manual/manual_17.html#SEC9.

    材质继承

    再深入介绍Material知识前,我们先介绍下材质的继承(越发的感觉到材质脚本的强大了吧?)

    行动:

    下面我们会通过两个材质,来演示材质的继承:

    1.将四边形材质设为 MyMaterial11:

    manual->begin("MyMaterial11", RenderOperation::OT_TRIANGLE_LIST);

    manual->position(5.0, 0.0, 0.0);

    manual->textureCoord(0.0,1.0);

    manual->position(-5.0, 10.0, 0.0);

    manual->textureCoord(1.0,0.0);

    manual->position(-5.0, 0.0, 0.0);

    manual->textureCoord(1.0,1.0);

    manual->position(5.0, 10.0, 0.0);

    manual->textureCoord(0.0,0.0);

    manual->index(0);

    manual->index(1);

    manual->index(2);

    manual->index(0);

    manual->index(3);

    manual->index(1);

    manual->end();

    2. 新的材质,留意texture_unit的名字:

    material MyMaterial11

    {

    technique

    {

    pass

    {

    texture_unit texture1

    {

    texture terr_rock6.jpg

    rotate_anim 0.1

    }

    }

    }

    }

    这里我们创建了一个新的材质,并使用了一个rotate_anim属性

    3. 创建在x轴上偏离15个单位距离的第二个场景节点 ,使用材质MyMaterial12:

    ent = mSceneMgr->createEntity("Quad");

    ent->setMaterialName("MyMaterial12");node = mSceneMgr- >getRootSceneNode()->createChildSceneNode("Node2",Ogre::Vect or3(15,0,0));

    node->attachObject(ent);

    4. 最后,我们再材质脚本中只要简单的将MyMaterial11的材质继承到MyMaterial12中来:

    material MyMaterial12 : MyMaterial11

    {

    set_texture_alias texture1 Water02.jpg

    }

    看到了吧,很简单的继承方法,并使用set_texture_alias替换了纹理

    5.编译,可以看到两幅转动的图片:

    clip_image020

    刚才所发生的:

    没,通过材质的继承机制,我们可以减少很多不必要的重复工作,可以大大地增加我们的工作效率,材质系统是个庞大工程,基础内容那个就介绍到此,还有很多东西值得去学习,更多内容参见如下:http://www.ogre3d.org/docs/manual/manual_14.html#SEC23.

    想必,你已经对一样东西感到迫不及待了吧?没错,Shaer,开始!

    固定管线与作色语言

    到目前为止,我们所使用的大部分工作都是基于被称作“固定渲染管线”上完成的。尽管这这样的管线上我们仍曾创造出许多非常多优秀的游戏,但是,毕竟限制太多,挡住了我们对更佳画面的渴望,于是,很大程度上可以自主对显卡直接沟通的C语言风格的Shader语言(作色语言,为了称呼习惯,下面不作翻译)诞生了。

    渲染管线

    要理解Shaders,我们首先应该理解我们的整体渲染管线流程。这是一个较复杂的过程,这里我就画蛇添足地做个简单的介绍和扩展,(如需详细了解,建议一定要哦掌握好相应的工程数学基础,再熟读计算机图形学,介时,方可出师),首先,模型本地空间的顶点由模型的世界变换矩阵转换入世界坐标系中,再依次经过摄像机矩阵,转换到摄像机空间,经投影矩阵压缩到产生近大远小的1*1*1的立体空间,再经视口变换转换到屏幕空间,到此为此,都将其称之为顶点处理,接下来便是将相应顶点光栅化,产生实际的像素点(注意,在DX10及以后这里还有一块神奇的GeometoryShader,有兴趣可以去了解一下强大的细分曲面等等东东),经过深度检测(注,现代的显卡已经相当地智能化,可以进行一步EarlyZ处理),Alpha检测,Scissor检测,模板检测,再加上AplhaBlend,雾处理,赋予纹理材质,到这里我们将其称之为像素处理过程(MS叫它PixelShader,Cg中则叫为FragementShader,公说公有理婆说婆有理,我们就暂且先听Ogre的,统一为FragementShader)最终显示

    clip_image022

    以上就是相关的处理过程表示

    附上敝人珍藏的一幅图,虽然还没把它贴到床头柜那么夸张,但从宏观上掌握GPU工作机理个人认为其实是比掌握单个引擎的使用要重要的多

    clip_image024

    可以注意到,每一代的新显卡首先要吹嘘的便是它的Shader新版本支持,先前已经有过介绍,Shader可以分成两大块,VertexShader(VS,顶点作色语言)负责的是顶点的变换工作,如果需要的话,如骨骼动画的数据处理,我们可以将其全部放在VS中处理,节省下的IO可以增加不少的FPS。PS则是处理具体的像素操作,这方面最主要的优点还是体现在光照处理上,而GeometoryShader由于MS对XpDX10的限制,它的普及基本还有很长一段时间,所以我们暂不讨论

    尝试写第一个Shader程序

    下面我们就开始写我们的第一段shader程序

    1. 改变材质:

    manual->begin("MyMaterial13", RenderOperation::OT_TRIANGLE_LIST);

    2.

    在ogre中,写shader我们必须首先确定5个信息

    @shder的名字

    @shader使用的语言

    @shader使用的源文件

    @shader的mian函数名字(shader中main函数不一定是main)

    @shader的编译版本(不同版本shader支持功能不同,必须考虑显卡的接受能力…)

    3.

    具体的定义实例可参见如下,不理解没关系,先打个照面混个眼熟

    fragment_program MyFragmentShader1 cg

    {

    source Ogre3DBeginnersGuideShaders.cg

    entry_point MyFragmentShader1

    profiles ps_1_1 arbfp1

    }

    4.

    vs需要传入参数,我们先要定义,如下面,我们设置了一个摄像机变换矩阵

    vertex_program MyVertexShader1 cg

    {

    source Ogre3DBeginnerGuideShaders.cg

    entry_point MyVertexShader1

    profiles vs_1_1 arbvp1

    default_params

    {

    param_named_auto worldViewMatrix worldviewproj_matrix

    }

    }

    5. 我们在材质脚本中引用相关的shader:

    material MyMaterial13

    {

    technique

    {

    pass

    {

    vertex_program_ref MyVertexShader1

    {

    }

    fragment_program_ref MyFragmentShader1

    {

    }

    }

    }

    }

    }

    6. Ogre3DBeginnersGuideShaders.cg in the media\materials\programs文件夹中创建shader

    7. 每一块shader看上去都像一个函数,不同的是我们使用关键词表示参数来负责输入输出,其中out表示返回的变量,如VS输出的out参数用于PS中处理,而PS处理完成之后就是最终的结果了,记住调用正确的shader函数名字,否则Ogre找不到它。是不是so easy呢?那接下来让我们开始跟加easy的FragementShader。(你要是E语足够好的话,熟悉之后多去翻翻老外的paper,就算一堆shader4.0也尽给它加上,绝对唬倒一片,呵呵)

    void MyFragmentShader1(out float4 color: COLOR)

    8.这里FS只是很简单地返回一种颜色:

    {

    color = float4(0,0,1,0);

    }

    9. 这里的FragementShader很简单地返回了一个固定值,Float4(0,0,1,0),主要的是VertexShader,传入的三个参数分别代表相应的position顶点位置(初始值,如局部坐标),输出完成的oPosition(完成变换后的顶点位置),及一个可以被认为全局变量的uniform变量worldViewMatrix(供给所有的顶点使用)

    void MyVertexShader1(

    float4 position : POSITION,

    out float4 oPosition : POSITION,

    uniform float4x4 worldViewMatrix)

    10. 在VS之中,输出position经worldViewMatrix变换后的点:

    {

    oPosition = mul(worldViewMatrix, position);

    }

    其实很简单,就是顶点乘以矩阵,个人比较习惯matrixWVP这样的表示

    11.

    结果,暂时是跟之前的固定管线效果一样,是不是不给力啊?!但实际上这里还是有很多门道的

    clip_image026

    刚才所发生的:

    下面,开始详细解释(狂轰滥炸,还望壮士千万顶住,建议先翻回3页前看看我们定义shdaer的代码);还记得前面所讲的Ogre对shader的五点要求么?

    首先开始将第二步,我们定义了了一段以fragment_program定义FragementShader,并在函数名字之后空格,加上了shader所使用的语言。

    对于早期的图像程序员,除了汇编级的Shader(传说中ASM,译者看过点基本语法,基本是简单的运算符,和汇编类似)他们没有其它选择。但这里读者不用担心,现在shader已经是高层的类C语言的天下了,就目前市面上主要存在有DX上的HLSL和OpenGL的GLSL,及Nvidia公司的Cg语言。考虑到Cg的通用性(同时适用与前两者平台),所以我们将在Ogre中基本使用Cg语言为主。

    话题转回,在表明要使用的shader名字及要使用的语言后,我们要接下来完成剩下的3个要求,类似我们常用的函数紧接的便是一对花括号{},其中是一种类似于常见的配置文件语法,前面是属性值,空格之后接上对应的属性值,首先给出要使的具体shader代码的源文件(注,这里不用给出完全路径,只需文件名即可,强大的ResourceManager会帮咱完成一切)

    接下来定义entry_point,即使用的shader函数名,在具体的shader代码中,参数只有一个“out float4 color : COLOR”,首先,前缀“out”表明此参数为一个返回值,它将提供给shdaer的下一步使用,参数的类型是“float4”,其实上它是一个数组,对于这里的颜色,实际填充的就是(R,G,B,A)颜色,分别代表红,绿,蓝和透明度,在color这个参数之后我们看到” : COLOR “这样的句式,因为对于渲染管线来说,我们传入的只是一个float4的数组,并不知道我们传入的是一个颜色值,所以我们通常在”: “号之后接上一个被称作” semantic “(语义)的代号,来表明此参数的具体用途,渲染管线会依此做出自动的数据判别

    最后一个参数,profiles,ps_1_1和arbfp1,要理解这个,我们又不得不从shader的历史源头开始说起,总之这个参数代表了DX及OpenGL所支持的历代shader版本,记住,版本越新,功能越多,但能用上你的程序的客户也会变得越少,to or not to be,it’s a question。

    以上内容,更多详细请见如下网址:

    http://www.ogre3d.org/docs/manual/manual_18.html

    在定义了FragementShader之后,VertexShader的语法也与此基本相同,最主要的不同只是在于向shader传入的参数default_ params块有所不同,由游戏里不断运动的无数模型,可以想象,参数在运行时时需要不断改变的,“param_named_auto “定义了类似的情况,ogre在运行时会自动的将新参数传入shader,ogre会根据给定的名字如worldViewMatrix或worldviewproj_matrix,自动的将前面所讲述过的WVP( WorldViewProjection矩阵传入)

    其它还有很多,详见如下

    http://www.ogre3d.org/docs/manual/manual_23.html#SEC128.

    我们将很快见到如何使用这些值。

    第4步,通常,我们定义一个材质为一个technique一个pass,我们并不需要再定义texture unit,vertex_program_ref将会取而代之,用这关键字定义具体使用的VertexShader函数名字,比如现在使用的MyVertexShader1,当然,如果需要的话我们还可以加入更多参数,这点放在以后再做。另外,FragementShader基本相同,就不再赘述。

    自己写一个Shader:

    正所谓万事开头难,现在我们终于可以开始着手实际的shader代码了

    第7步:

    可以看到,在函数体内我们只是简单的返回了一个float4数组(0,0,1,0),即一个透明打蓝色,结果是一切都会被渲染成蓝色

    接下来是VS,这里值得注意的是第三个参数,worldViewMatrix,参数前的“语义“”uniform“代表了这个参数是的生命周期不同前面两个,它其实存在整个draw call之中(这个也很容易理解,比如一个mesh,渲染时它通常只有一个node的世界坐标转换矩阵,但时它可以有很多的顶点来共用这个矩阵)。当然,在渲染下一个物体时,ogre会自动传参将worldViewMatrix改变,这里勿需担心

    第9步,

    来到了VS具体的函数体,这里世界上也是很简单地做了一个顶点与矩阵相乘(也就是渲染管线中提到的变换过程)。当然,在熟悉这一切之后,我们会在这里做更多,进而实现非凡的效果。

    Shader中的纹理

    厌倦了蓝色的四边形,那我们就将其加上纹理吧

    1. Ctrl+C、Ctrl+V大发,创建一个新材质MyFragementShader2

    texture_unit

    {

    texture terr_rock6.jpg

    }

    2.

    这回要传入新的参数,用于纹理采样器(由于缩放的需要,纹理需要采样)及纹理坐标,

    同样,这里使用了semantic,从TEXCOORD字面上就很好理解,这是一个纹理坐标,

    纹理同前面的矩阵变换一样,是uniform,这点不难理解。:

    void MyFragmentShader2(float2 uv : TEXCOORD0,

    out float4 color : COLOR,

    uniform sampler2D texture)

    3.

    这回我们不再简陋地使用单色了,用tex2D可以采样出texture的uv坐标点上具体颜色

    4. 在VS中同样要加上texcoord的处理,要使它不敢当话,FS(FragementShader)那边可就只能干着急了

    void MyVertexShader2(

    float4 position : POSITION,

    out float4 oPosition : POSITION,

    float2 uv : TEXCOORD0,

    out float2 oUv : TEXCOORD0,

    uniform float4x4 worldViewMatrix)

    5. 同样的VS处理:

    oPosition = mul(worldViewMatrix, position);

    6. 这回我们还要输出uv坐标:

    oUv = uv;

    7. 在代码中调用新的材质MyFragementShader2,结果可见如下

    clip_image028

    刚才所发生的:

    到目前为止,我们所做的只是添加了一幅纹理而已,没有什么太大的神奇,核心不外乎是VS读入导出了纹理坐标,FS再根据纹理坐标做了具体的显示

    具体在渲染管线中的处理

    VS处理完顶点变换进入指定的观察空间后,渲染管线就会对它们进行光栅化,其后,数据会被传入到FS之中,写过代码也可以看出,我们的顶点只有四个,但实际的像素点却有那么多,uv坐标是怎么产生的呢?这就涉及到一个插值的问题,精度是不能100%保证的,所以如果你用过一些DDS软件或者引擎工具的话,可能会发现光照处理的选项会有VS和PS(FS)之别,这其中其实也就是涉及到精度与效率的考虑

    clip_image030

    插值,同样也会被用于顶点颜色

    结合顶点色与图片纹理坐标的实践

    分别创建MyVertexShader3和MyFragmentShader3

    对颜色进行插值

    To see the effect of interpolation better, let's replace the texture with colors.

    为了突出效果,我们这里暂不显示纹理。

    首先,需要在代码里稍稍做一下改动

    1.

    Again,做前面同样的工作.

    2.

    去掉纹理

    3.

    以颜色代替之:

    manual->position(5.0, 0.0, 0.0);

    manual->color(0,0,1);

    manual->position(-5.0, 10.0, 0.0);

    manual->color(0,1,0);

    manual->position(-5.0, 0.0, 0.0);

    manual->color(0,1,0);

    manual->position(5.0, 10.0, 0.0);

    manual->color(0,0,1);

    4.看代码,VS同样做了些小的改动:

    void MyVertexShader4(

    float4 position : POSITION,

    out float4 oPosition : POSITION,

    float4 color :COLOR,

    out float4 ocolor :COLOR,

    uniform float4x4 worldViewMatrix)

    {

    oPosition = mul(worldViewMatrix, position);

    ocolor = color;

    · }

    5.简单地使用Pipeline计算出来的插值

    void MyFragmentShader4( float4 color : COLOR,

    out float4 oColor : COLOR)

    {

    oColor = color;

    }

    6.没敲错代码的话,你会看到很漂亮的渐变效果

    clip_image032

    刚才所发生的:

    我们分别给四边形的几个顶点去除了纹理坐标而加上了不同的颜色,

    然后简单地使用了渲染管线插值的结果,想想三原色,儿时玩的颜料,这样,或许你可以更好的理解插值

    采用实际的模型进行处理

    渲染四边形如此,将问题扩展到渲染模型,其实方法同样如此

    其实材质可以很方便地运用于Mesh,这点ogre做得很好,其实上,我们做3D程序,很大程度上大部分的工作便是在于如何让这些mesh显示,活动

    1.删去产生四边形的代码.

    2.将 Sinbad.mesh导入到场景中来, 并将材质MyMaterial14赋给他:

    void createScene()

    {

    Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()- >createChildSceneNode("Node1");

    Ogre::Entity* ent = mSceneMgr->createEntity("Entity1","Sinbad. mesh");

    ent->setMaterialName("MyMaterial14");

    node->attachObject(ent);

    }

    3.

    老规矩,F5,运行

    clip_image034

    刚才所发生的:

    这里发生的一切读者都应该心中有数了吧?没错,仅仅改变了下贴图而已

    使模型在X轴向上产生缩放效果

    Fragment掌握的已有一定基础了,让我们接下来把玩一下VertexShader(VS)

    行动,加入效果

    这实际上只需要少数几行代码

    1. 这回,创建一个新的,类似的VS函数,废话不说,看代码:

    material MyMaterial17

    {

    technique

    {

    pass

    {

    vertex_program_ref MyVertexShader5

    {

    }

    fragment_program_ref MyFragmentShader2

    {

    }

    texture_unit

    {

    texture terr_rock6.jpg

    }

    }

    }

    }

    2. 仅仅增加一个pulseTime参数

    vertex_program MyVertexShader5 cg

    {

    source Ogre3DBeginnerGuideShaders.cg

    entry_point MyVertexShader5

    profiles vs_1_1 arbvp1

    default_params

    {

    param_named_auto worldViewMatrix worldviewproj_matrix

    param_named_auto pulseTime time

    }

    }

    3.

    这里并不需要改动任何c++代码,我们只需在新的shader函数中,加上一点小小的数学改造

    void MyVertexShader5( uniform float pulseTime,

    float4 position : POSITION,

    out float4 oPosition : POSITION,

    float2 uv : TEXCOORD0,

    out float2 oUv : TEXCOORD0,

    uniform float4x4 worldViewMatrix)

    {

    oPosition = mul(worldViewMatrix, position);

    oPosition.x *= (2+sin(pulseTime));

    oUv = uv;

    }

    4.结果是不是很囧

    clip_image036

    刚才所发生的:

    我们使模型在X轴上产生了拉伸运动效果,

    在VS中我们定义的第二个参数用于将当前的时间传入,我们使用sine函数,实现了模型整体在x轴上的1~3倍的缩放。

    通过这样的尝试,你可以明白,通过vs中我们可以很轻易地改变顶点数据,这其实也就是许多游戏特效的奥秘之所在。

    总结一下:

    我们学到了

    @如何创建材质

    @如何将贴图材质应用于具体模型

    @如何创建shader并将它们在在材质脚本中使用

    @理解渲染管线,并尝试修改具体的顶点

    在下一章,我们将学习神奇的Compositor,传说中的后处理。

  • 相关阅读:
    341. Flatten Nested List Iterator
    667. Beautiful Arrangement II
    953. Verifying an Alien Dictionary
    1704. Determine if String Halves Are Alike
    MySQL数据库的数据类型详解【转】
    Dart之数组(List)的相关方法总结【转】
    Flutter解决点击非输入框时关闭键盘问题及TextFormField焦点切换问题【转】
    Flutter Switch开关【转】
    Image Picker
    Flutter按钮添加背景图片及文字【转】
  • 原文地址:https://www.cnblogs.com/Zephyroal/p/2060562.html
Copyright © 2020-2023  润新知