o3d教程4 - 纹理映射
来源:互联网 发布:教皇的权力 知乎 编辑:程序博客网 时间:2024/05/29 18:24
这一章讲纹理,纹理,不能顾名思义了,其实就是一张图片,我们要做的就是把这张图片贴到模型上面,从而让模型一下子生动起来,说得有文采点就是栩栩如生,这个过程就叫纹理映射(也可以叫贴图)。纹理映射有时候能产生非常神奇的效果,比如说凹凸贴图,和凹凸贴图衍生出来的法线贴图,能够让只有几百个几千个多边形的模型产生几万个,几十万个多边形的模型的效果。
接下来就要介绍怎么创建一个纹理采样器(texture sampler),如何设置采样的属性,然后把一张图片(纹理)成功地贴到那个立方体上面,图片的格式可以是TGA,JPEG,PNG,DDS,其中像TGA,PNG这样的格式是支持半透明的,可以用来模拟阴影(当然只是简单地模拟)。
纹理采样器负责怎么把一张图片上的各个像素映射到模型的像素上,反过来说,就是为每个像素选取纹理像素。看似很简单,但是当显示的图片大于(或小于)要覆盖的那个面时,就得就结了,这个不能一一映射了,那得怎么做呢,这是门学问,我们可以通过设置这个纹理采样器的各个属性来达到较好的效果。
Address Mode
首先是addressModeU和addressModeV属性,这两个属性决定了当纹理坐标超出(0,1)后该怎么做。纹理坐标,顾名思义,就是在纹理上的坐标,这是我们截取纹理中的一部分是要用到的。它的范围是(0,1)。其中左下角的坐标是(0.0 , 1.0),右上角的坐标是(1.0 , 1.0)
这两个属性有下面四种可能值
Value
Meaning
WRAP
(默认为此值)重复贴图来填充贴图区域
MIRROR
重复贴图,当遇到UV的边界时反向(UV边界为0.0或者1.0)
CLAMP
用贴图的最后一行像素,重复覆盖没贴到的区域
BORDER
超过的贴图区域,用一种特殊颜色的边标记出来
Minification Filter
Minificationfilter,即minFilter这个属性具体说明了怎么把纹理贴到一个像素比它少的模上面(这个上面有提到过),这个属性可能有下面3个值
Value
Meaning
POINT
用最近的那个像素
LINEAR
线性插值
ANISOTROPIC
各向异性过滤
这三个滤镜效果是单调递增的,具体是怎么实现的,这里不多讲了(其实也不会讲- -),想了解的话可以去看图像处理方面的书籍。。。。
MipmapFilter
由于处理缩小纹理比处理放大纹理复杂多了,如果只是简单地取最近的那个像素,或者说只是和这个像素的周围几个像素简单的比较一下,获得平均值就插进去的话,会严重降低图片缩小后的质量,而如果去周围大量的像素,做大量的计算又会严重降低程序效率。于是有才的人就想到了mipmap这个方法。它先将这张图片按原来尺寸一半一半地压缩,举个例子,原来的是64*64的图片,就压缩成一张32*32的,一张16*16的,一张8*8的......当这张图片需要映射成20*20的大小的,就取32*32和16*16这两张图片来计算出20*20大小的图片,这比只是计算单张图片的效率和效果要好得多.
下面是这个参数可能的值
Value ofmipFilter
Which Mipmap Is Used
NONE
不用mipmap
POINT
取与需要映射的大小最近的那张图片,然后依据上面的minification filter进行过滤
LINEAR
取与需要映射的大小最近的两张图片,为每张图片按照上面的miniFilter进行过滤,然后把这两张图片一起进行线性插值等到最终的图片
MagnificationFilter
放大纹理的处理就比较简单了,有下面两种可能的值。
Value
Meaning
POINT
Use the closest pixel.
LINEAR
Perform a linear interpolation between neighboring pixels and use the result.
跟minFilter差不多。
BorderColor
当前面的address mode设成border时,这个属性就派上用场了,它可以用来设置那个边框的颜色。
Anisotropy
当前面的minFilter是Anisotropy时,这个值就是决定了各向异性过滤的质量。
这些属性介绍完了,下面就举个实例,把那张求是潮的logo贴到那个立方体上。
首先要建立一个texture sampler,同shape 和 material 一样,这也是一个Object,非常好地体现了"面向对象"的思想。
g_sampler =g_pack.createObject('Sampler');
g_sampler.minFilter= g_o3d.Sampler.ANISOTROPIC;
g_sampler.maxAnisotropy= 4;
可以看到第一句就是建立sampler对象,后面两句就是设置上面提到的属性,minFilter设为各向异性过滤,maxAnisotropy设为4。
这样一个简单的sampler就建立了,但是比较打击人的是这个还只是设了下最基本的参数,这个参数设了给谁用呢,又是在哪里把图片贴到那个模型上面去的呢
恩~在o3d里面纹理贴图就是在pixelshader里面用了一个函数tex2D把图片像素一个个根据上面设好的各个参数填进已经经过vertex shader变换和primitive assembly(就是shape一章里讲过的把各个顶点按照一定规则连接成图元的操作)了的东西里面。而传给tex2D的参数有两个,一个是一开始提到的纹理坐标(texCoord),还有一个就是采样器的各个参数了(这里面也包括了整个纹理素材),要把js中设的参数传到shader里面,o3d中的做法是先在shader里面建一个变量,比如texSampler0,然后在js中用material的getParam方法获取这个texSampler0变量,具体的如下:
effect.createUniformParameters(material); //这句话是不可少的,否则就不能获取shader里面设的变量了
var samplerParam = material.getParam('texSampler0');
samplerParam.value = g_sampler; //把g_sampler的参数赋给samplerParam
这里的material 和 effect就是前面几章为那个立方体建立的材质对象。所以可把上面几句代码加在原来的建立material和effect的代码的后面(具体的shader代码在最后放出来)
下面要做的是把纹理资源载入内存。
o3djs.io.loadTexture(g_pack,textureUrl, function(texture) {
// set the texture on the sampler object to the newly created texture
// object returnedby the request.
g_sampler.texture = texture; //可以看到这里把纹理资源也附到sampler里了以供shader使用
})
其中function(texuture)。。。。。是回调函数,这里声明成匿名函数了(js非常好用的一个特性),如果要处理的量比较大的话,可以独立出来写成一个函数。也可以在其它纹理载入的时候使用。textureURL顾名思义就是图片地址了。
最后是声明各个顶点的纹理坐标,前面说到过要传给shader的,这个和声明顶点坐标差不多(顶点坐标数组也得相应的改过,不能几个面公用同一个顶点了,具体的建sample里的代码吧,再放出来就太长了= =)
var texCoordsArray = [
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
1, 1,
0, 1,
0, 0,
1, 0,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 1
];
var texCoordsBuffer = g_pack.createObject('VertexBuffer');
var texCoordsField = texCoordsBuffer.createField('FloatField', 2);
texCoordsBuffer.set(texCoordsArray);
streamBank.setVertexStream(
g_o3d.Stream.TEXCOORD, // semantic
0, // semantic index
texCoordsField, // field
0); // start_index
不多做解释了。
最后的最后:
Shader代码
// World View Projection matrixthat will transform the input vertices
// to screen space.
float4x4 worldViewProjection : WorldViewProjection;
// The texture sampler is used to access the texturebitmap in the fragment
// shader.
sampler texSampler0;
// input for our vertex shader
struct VertexShaderInput {
float4 position : POSITION;
float2 tex : TEXCOORD0; // Texturecoordinates
};
// input for our pixel shader
struct PixelShaderInput {
float4 position : POSITION;
float2 tex : TEXCOORD0; // Texturecoordinates
};
/**
* The vertex shader simply transforms the inputvertices to screen space.
*/
PixelShaderInput vertexShaderFunction(VertexShaderInputinput) {
PixelShaderInput output;
// Multiply the vertex positions by theworldViewProjection matrix to
// transform them to screen space.
output.position = mul(input.position,worldViewProjection);
output.tex = input.tex;
return output;
}
/**
* Given the texture coordinates, our pixel shader grabsthe corresponding
* color from the texture.
*/
float4 pixelShaderFunction(PixelShaderInput input):COLOR {
return tex2D(texSampler0, input.tex);
}
// Here we tell our effect file *which* functions are
// our vertex and pixel shaders.
// #o3d VertexShaderEntryPoint vertexShaderFunction
// #o3d PixelShaderEntryPoint pixelShaderFunction
// #o3d MatrixLoadOrder RowMajor
Shader的代码就不解释了,下一章会比较详细地讲下shadinglanguage和图形渲软管线到底是怎么样的,下下章可能会讲光照,光照就是都在shader里面计算了,这个纯考数学物理知识啊,不过幸好已经有前人弄好的公式给我们套了。然后就是讲下阴影(shadow)的生成了,注意的是并不是有光照就会自然而然地产生阴影了,阴影的处理是游戏中比较关键的,也是很占资源的一个地方。阴影处理的好的话游戏的真实性倍增啊啊。。。。
还有要说的就是shapes和这章的纹理映射以后不会这么详细地用到,不会让你一个个顶点地去定义,更多的是载入一个用3d软件诸如3DMAX 或maya做的模型,或者是用自带的函数简单地创个球,毕竟像这次这样创建一个带有贴图的立方体会让人崩溃的。
刚刚在整sample的时候发现纹理素材载入的时候总是出现未知错误,折腾了半天都没找到原因,后来复制到别的地方发现又莫名其妙地可以用了,总算找到了原因:路径中不能有中文,哎,感叹一下,其实很多游戏安装路径中也是不允许有中文的= =。
- o3d教程4 - 纹理映射
- o3d纹理映射(转)
- openGL教程 纹理映射
- OpenGL教程之纹理映射
- OpenGL教程之纹理映射
- opengl 教程(16) 纹理映射
- opengl 教程(16) 纹理映射
- o3d教程2 - shapes
- Direct3D9 教程05:使用纹理映射
- NeHe OpenGL教程 第六课 纹理映射
- NeHe教程第6课纹理映射
- Andriod OpenGL 教程 06 - 纹理映射
- DirectX 9.0 C++ 教程 Texture 纹理映射
- OpenGLES 1.1教程(四)-纹理映射
- OpenGLES 1.1教程(四)-纹理映射
- 【Qt OpenGL教程】06:纹理映射
- NeHe教程第6课纹理映射
- o3d教程3 - 模型变换
- C语言一些常用语句
- C/C++ Build Errors
- Symbian界面编程中关于Edit焦点切换的问题
- NetBeans6.8下建立Ruby调试环境
- Qt通过dom方式修改xml
- o3d教程4 - 纹理映射
- Java多线程编程要点 (一)
- 网站链接和待总结
- linux 启用root及更改密码
- winhddif blog
- Javascript判断滚动条是否到达页面的尾部
- svn在linux下的使用(svn命令)
- 今天北京的天空是湛蓝的
- 对JavaScript中原型的理解