本准备先说光照相关实现,但是发现对那个模型实在看不下去了,于是先绘制纹理。
先看下基本纹理贴上去的显示效果。具体模型图请看上篇文章的实现,这篇只讲纹理实现。
我们常见的纹理绘制差不多如下,先写一个纹理坐标,然后是一个顶点坐标,GL.TexCoord2(1.0f,1.0f);GL.Vectex(1.f,1.f,1.f)。先说一下纹理坐标与顶点坐标的对应处理关系,为了好理解,我们只说二维纹理。先看下图。
我们设置一张16*8的纹理,如上图,我们设置GL.TexCoord2(1.0f,1.0f)就是在(16,8)位置,超出1的部分,会复制超出部分,如上图设置GL.TexCoord(2.0,2.0)就会在图上一共显示四张图,同理我们只要纹理的右下部分四分之一,那坐标应该分别是(1,0.5),(0.5,0.5),(0.5,0)(1,0).
在上个模型中,我们可以看到我们有很多的模型需要纹理贴图,如果采用一般的方法,需要在每个立方体的面上去计算我们相应的纹理坐标,这样花费时间太大,这样我们采用opengl里提供的自动生成纹理。首先要指定以什么样的模式(既什么样的算法)来生成纹理坐标。可以指定几种纹理坐标生成模式:GL_OBJECT_LINEAR, GL_EYE_LINEAR, GL_SPHERE_MAP等。
在这里我们只需要采用最容易理解的纹理生成模式。GL_OBJECT_LINEAR,在这种模式下,指定四个参数,p0,p1,p2,p3,对应顶点(x0,y0,z0,w0),生成的纹理坐标为p0*x0+p1*y0+p2*z0+p3*w0。
我们再来看我们的需求
1。要对立方体的各面自动生成纹理。
2。我们贴地面的,要像砖是一块一块的,而贴墙时,只需要贴一张。如最上图所绘,左下角与右上角分别对应的纹理坐标在贴砖是一块一块的应该是((0,0),(n,m))《m>0,n>0》,而贴墙时应该对应((0,0),(1,1))。
借用上篇的图。
第一点,根据我们的p0*x0+p1*y0+p2*z0+p3*w0算法来看,比如贴地面时,也就是垂直于Y轴时,用到的是图上的(4,5,1,0)这个面。
想象一下对应关系,纹理的S轴坐标对应是1-0这条线,T轴坐标对应的是1-5这条线,那么对应s轴大致如下[x;0.;0.;0],T轴坐标大致如下[0;0.;y;0].各面按照这样得到对应的参数。相关代码如下。
1 member this.GenTexture(vector:Vector3,ball) = 2 GL.Enable(EnableCap.TextureGenS) 3 GL.Enable(EnableCap.TextureGenT) 4 GL.TexGen(TextureCoordName.S,TextureGenParameter.TextureGenMode,int TextureGenMode.ObjectLinear) 5 GL.TexGen(TextureCoordName.T,TextureGenParameter.TextureGenMode,int TextureGenMode.ObjectLinear) 6 let mutable xa = [|1.f;0.f;0.f;0.f|] 7 let mutable xy = [|0.f;1.f;0.f;0.f|] 8 let mutable x,y,z=1.f,1.f,0.f 9 if ball then z <-0.5f 10 if vector = Vector3.UnitY || vector = -Vector3.UnitY then 11 if ball then 12 x<-1.0f/Vector3.Subtract(v8.[0],v8.[1]).Length 13 y<-1.0f/Vector3.Subtract(v8.[0],v8.[4]).Length 14 xa <- [|x;0.f;0.f;z|] 15 xy <- [|0.f;0.f;y;z|] 16 if vector = Vector3.UnitZ || vector = -Vector3.UnitZ then 17 if ball then 18 x<-1.0f/Vector3.Subtract(v8.[0],v8.[1]).Length 19 y<-1.0f/Vector3.Subtract(v8.[0],v8.[3]).Length 20 xa <- [|x;0.f;0.f;z|] 21 xy <- [|0.f;y;0.f;z|] 22 if vector = Vector3.UnitX || vector = -Vector3.UnitX then 23 if ball then 24 x<-1.0f/Vector3.Subtract(v8.[0],v8.[4]).Length 25 y<-1.0f/Vector3.Subtract(v8.[0],v8.[3]).Length 26 xa <- [|0.f;0.f;x;z|] 27 xy <- [|0.f;y;0.f;z|] 28 GL.TexGen(TextureCoordName.S,TextureGenParameter.ObjectPlane,xa) 29 GL.TexGen(TextureCoordName.T,TextureGenParameter.ObjectPlane,xy)
其中ball参数表示贴墙这种一面只帖一张纹理。在ball时,我们可以看到我们的p0,p1,p2,p3,p3=0.5,这是因为我的矩形的画法所导致的(前看上篇所叙),比如我要生成宽为8的立方体,其中我画顶点是x=-4,x=4这种画法,如果不加0.5,那么我的帖图就会是左下到右上是(-0,5,-0,5),(0.5,0.5)这种。
在opengl是状态机模式,记的每画一面打开相应的状态后,在画完后,需要关掉,不然会影响下一部分的贴图。绘制部分的代码如下。
1 member this.DrawTexTure(ts:int*int*bool,vector:Vector3,indexs:int []) = 2 let ind,txtId,ball = ts 3 if txtId <> 0 then 4 GL.BindTexture(TextureTarget.Texture2D,txtId) 5 this.GenTexture(vector,ball) 6 GL.Normal3(vector) 7 GL.DrawElements(BeginMode.Triangles,6,DrawElementsType.UnsignedInt,indexs) 8 GL.Disable(EnableCap.TextureGenS) 9 GL.Disable(EnableCap.TextureGenT) 10 GL.Disable(EnableCap.TextureGenR) 11 GL.Disable(EnableCap.TextureGenQ)
最后附上源码与可执行文件。操作方式和网游一样,鼠标右键按下加鼠标上下左右移动是视角.EDSF行走。
下一章节会给大家讲到光照的运用。