OpenGL笔记(一)

这篇博客介绍了OpenGL的坐标系,包括OpenGL与Android设备屏幕坐标系的区别,以及如何进行坐标变换。同时讲解了纹理过滤的概念,如最近邻过滤和双线性插值,以及MIP贴图技术。此外,详细阐述了正交投影矩阵的创建,包括Android中如何使用`android.opengl.Matrix`来构建和应用正交投影。
摘要由CSDN通过智能技术生成

 

坐标系问题:

openGL的坐标系,这个是openGL的二位坐标系(而不是手机屏幕的坐标系),openGL的二位坐标系是把一个显示器屏幕看作是一个归一化设备,就是宽高为2的显示平面,openGL坐标以屏幕中心为原点,x为向右为正,y为向上为正。各个角的坐标如下图所示。

而Android设备屏幕的坐标系实际是以左上角为原点,x向右为正,y向下为正。

基于以上的差别,所以在使用openGL绘制图片,设置顶点时,要以openGL坐标去绘制。然后openGL自然会根据Android设备的屏幕坐标系做一个变换,就是先去归一化。为了方便解说,就按720*1280的屏幕来讲,且是竖屏。如果openGL坐标为(0,0),那么变换为屏幕坐标就是 (720/2*0 ,1280/2*0),然后再做平移,向右下角平移,就是x平移720/2,y平移1280/2,  平移后的值就是(720/2+720/2*0, 1280/2+1280/2*0)。

 

openGL坐标系的归一化在不同宽高比的显示器上的显示问题。

比如一个在openGL中的坐标系,一个圆绘制到openGL的坐标系中,那么在opengl坐标系中也是圆的,但是在手机屏幕上,屏幕宽高并不是1:1的,例如720*1280的屏幕,如果在openGL坐标系中,直径为0.2,那么在屏幕中,横向的直径就是0.2*720,而竖向的直径就是0.2*1280,那么这个在openGL中的圆,绘制到手机屏幕上就是一个椭圆了。要解决这个问题,就是绘制到openGL坐标系时需要把手机屏幕的宽高比考虑进去,这样就可以保证绘制手机屏幕上是一个圆。

 

OpenGL管道概述

 

顶点着色器和片段着色器:

顶点就是构成点,线,面的顶点。一个方形的图片,可以有六个顶点构成(其中有2个是重复的)。一个面是由很多像素组成的,如果只有顶点着色器的话,那么就时能绘制六个顶点了,实际上肯定是要绘制很多方形包含的图片的所有点的,所以需要片段着色器,这个片段着色器就是用于绘制方形包含的所有像素点。首先一个顶点可以有位置属性和颜色属性。使用attribute关键字来修饰。方形中的各个点都是有坐标的,但是并不用去定义,只需定义顶点,而方行中的其他点,会自动插值,然后送到某个变量,然后顶点着色器可以使用这个插值变量中的值去做其他事。也可以让一个顶点对应某些向量,如果一个顶点对应一个纹理坐标,那么这些方形内的点插值时,也会根据顶点对应的那个纹理坐标,插值得出该点对应的纹理坐标,然后输出到顶点着色器的某个varying变量中,这个变量需要在代码中取出,然后告诉opengl,然后纹理坐标的插值就会送到这个varying变量,全局的varying变量是可以和片段着色器共享的,片段着色器就可以使用这个纹理坐标,去取纹理数据了,然后就可以读出纹理数据中的rbg值,然后就可以输出颜色,就可以逐个点亮图片(纹理)中的每个像素(片段)

 

顶点着色器绘制三角形

1.opengl绘制面的时候是用一个三角形作为面的单元的,而一个三角形需要三个顶点,在定义顶点数组时,重复的顶点不一定要重复定义,如三角扇的定义,就是三角形,定义六个点。每个顶点可以有其他属性,随便自己定义。自定义的属性(大多是向量,如vec3,vec4)跟每个顶点对应好,就是顶点的位置(vec3,vec4)跟自定义的属性一起定义。顶点的各种数据定义跟顶点着色器的定义是分开的。最后要把顶点数据和顶点着色器绑定。

顶点数据和顶点着色器绑定:

如一个数组时包含了位置信息和颜色信息的,排列顺序是x,y,r,b,g

那么需要将数组中的位置信息和颜色信息绑定顶点着色器的位置属性和颜色属性。

顶点着色器中有用于接收输入的变量,还有输出的变量,输出变量是输出给opengl的,我们不用管,只管把要输出的值赋值给定义好的那个输出变量就好了。

绑定位置属性:

首先要在顶点着色器中定义一个全局的用attribute修饰的vect4(虽然是二维的但是还是要使用vect4来接收,应该会填入两个值,然后其他两个默认为0,然后输出的时候如果是指定绘制2d的话估计会写入z=0,w=1),如:

着色器被链接后,我们就只需要加入一些代码去获取属性位置。

this.aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);

然后把这个属性和数据绑定,即在调用顶点着色器时,把数据付给这个属性:

this.vertexData.position(0);

GLES20.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT, false, STRIDE, this.vertexData);

其中vertexData是一个BufferFloat类型。首先要缓冲区的位置,就是要从缓冲区的哪个位置开始读。然后指定一个向量包含了多少个数据,如果是二维的,则一个位置向量包含了两个数据,就是(x,y)。如果顶点数组中不是所有数据都是位置信息的话,如在这里顶点数组就包含了颜色信息,那么要告诉opengl怎么跳过这些颜色信息去取下一个位置数据。STRIDE就是跳过多少个字节,跳过位置信息占字节数加上颜色信息占字节数。

同样的,把顶点数组中的颜色数据和顶点着色器中的颜色属性也是这样绑定。就两句搞定

看看如何定义顶点着色器的属性:

接着必须对应的建立对应的顶点着色器,假设raw文件夹下的顶点着色器的文件名是simple_vertex_shader.glsl:
attribute vec4 a_Position;    //顶点数据绑定这个属性后,就会逐个把顶点的位置信息传给这个变量
attribute vec4 a_Color;   //顶点数据绑定这个属性后,就会逐个把顶点的颜色信息传给这个变量
varying vec4 v_Color;   //这是一个可以跟片段着色器共享的变量。而且如果某个属性(这里的a_Color)在main中赋值给这个变量,那么这个变量随着位置的变化(就是面中的点的位置),而不断插值,就是根据距离三个顶点的远近来对a_Color作线性插值,远近某个顶点,那么那个顶点对应的a_Color属性的权重就越大,就是说颜色会偏向a_Color(前提是片段着色器使用v_Color作为输出)
void main()                    
{
    v_Color=a_Color;
    gl_Position = a_Position; //gl_Position是一个输出变量,是opengl定义好的。opengl只认这个,所以要把输入处理后,传给这个输出变量
    gl_PointSize = 10.0;//这也是一个输出变量
}  
我们加入了一个新的属性a_Color,也加入了一个叫做v_Color的新varying。上一篇已经讲过varying是一个特殊的变量类型,它把给它的值进行混合并把这些混合的值发送给片段着色器。

 

 

我们把varying也加入片段着色器,在raw文件夹下创建simple_fragment_shader.glsl:

precision mediump float;

varying vec4 v_Color;//这是和顶点着色器共享的,就是这个值和顶点着色器的值是一个样的
void main()                     

{                              
    gl_FragColor = v_Color;//gl_FragColor是片段颜色输出变量
}

 

线性代数-矩阵

矩阵相乘:A*B=C

相乘结果:相乘后的结果是等于C的行数为A的行数,C的列数为B的列数。C[0,0]=A的0行和B的0列相乘,然后乘积相加,就是A[0]*B[0]+A[1]*B[1]+...+A[Na][BNb]。很明显,如果两个矩阵要相乘的话,必须要A的列数等于B的行数,否则,就无法正确得到结果。

将向量作变换:如果一个向量需要作矩阵变换,需要用向量乘以一个矩阵。如果把向量放在乘号左边,那么向量应该要作为一行四列的矩阵,然后乘以一个四行四列的矩阵。如果向量放在乘号的右边,那么向量应该要作为四行一列的矩阵。

单位矩阵,就是任意矩阵乘以单位矩阵都等于原来的矩阵,所以单位必须是方形矩阵,就是行数等于列数,因为矩阵从单位矩阵左边或者右边乘都可以。单位矩阵还要保证一条对角线的元素全为1,其他元素为0。

作矩阵变换的技巧:因为矩阵相乘是行和列对应的元素的乘积相加的,如果想要增加偏移量的话,就在单位矩阵的基础上作些改动就好,将某个0改成非0。

 

 

三维空间:

使用透视法。原理就是,视野角度相同时,画面越远,看到的范围就越大,物体看起来就越小。透视法的原理就是使用w,w代表距离远近,距离越远,w越大,w默认为1。通过透视变换后的坐标是(x/w, y/w, z/w,w),就是x,y,z都除以w分量,那么本来不在画面范围的点,都向画面中心(渲染中心)靠近,就会使更多的点渲染到可见画面中,而远处的物体渲染到画面时也会缩小。

 

三维透视法:

这是一个透视投影使用到的投影矩阵。就是将透视锥视体中的三维物体的坐标归一化,就是投影到近平面上。

近平面:可以看做是屏幕,可以使用手机来做例子。

远平面:可以是无限远的平面。这个远平面的位置可以根据实际应用来确定(就是看想要把最远多远的地方的物体投影到近平面,按经验来说,并不是无线远的物理都能被看到的),就是根据需要去确定锥视体的大小。

视野:就是一个角度,每个摄像头都有固定的视角,这个视角小于180。

上图是视锥体的俯视图

由上面的解释可以知道,透视法,就是将锥视体中所有物体投影到近平面。其实就是成像。比如一张照片是3968*2240,那么近平面的大小就是3968*2240(这里使用像素作为尺寸单位)。那么可以把近平面大小作为常量,当视野变化时(视角边宽或变窄,以焦点为顶点的角等于视角的大小,就是表格中的a),焦点位置就会变化,焦距也会变化

看那个投影矩阵,对x和y的变换只与屏幕宽高比和视角大小有关,而对于z的变换,和f,n,w(w默认是1)有关,f和n就是焦点到远近平面的距离。w的变换跟z有关,变换后的w=-z。

注意:经过投影矩阵的变换后,还需要做透视除法,就是让x,y,z分别除以w。得到的坐标就是最终投影到近平面上的坐标。

 

正交投影:

要定义正交投影,我们将使用Android的Matrix类,它在android.opengl包中。这个类有一个称为orthoM()的方法,它可以为我们生成一个正交投影。

 

我们来看一下orthoM()参数:

 

orthom(float[] m,int mOffset,float left,float rigth,float bottom,float top,float near,float far)

 

float[] m:目标数组,这个数组长度至少有16个元素,这样它才能存储正交投影矩阵。

int mOffset:结果矩阵起始的偏移值。

float left:X轴的最小范围。

float right:X轴的最大范围。

float bottom:Y轴的最小范围。

float top:Y轴的最大范围。

float near:Z轴的最小范围。

float far:Z轴的最大范围。

 

当我们调用这个方法的时候,它应该产生下面的正交投影矩阵:

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值