OpenGL/EGL练习

public class LessonOneRenderer implements GLSurfaceView.Renderer

mModelMatrix | 浮点数组 | 用于把模型从对象空间移动到世界空间(坐标系?)

mViewMatrix | 浮点数组 | 可以看作是摄像机,用于把模型从世界坐标系转换到eye space(这个怎么翻译?)

mProjectionMatrix | 浮点数组 | 用于把模型投影到二维坐标系(2d viewport)

mMVPMatrix | 浮点数组 | 保存最后组合好的矩阵, 会被传给shader 程序

mTriangle1Vertices, mTriangle2Vertices, mTriangle3Vertices | FloatBuffer | 保存模型数据

mMVPMatrixHandle | 整型 | ?

mPositionHandle | 整型 | ?

mColorHandle | 整型 | ?

mStrideBytes | 整型 | 初始化为7 * mBytesPerFloat,因为 X, Y, Z,R, G, B, A是七个数据

LessonOneRenderer 构造函数,用于初始化模型数据

final float[] triangle1VerticesData = ... 定义三个本地浮点数组作为三角形模型的数据

三个三角形的顶点坐标都是一样的(-0.5, -0.25), (0.5, -0.25), (0, 0.5)

第一个的颜色是 red, green, and blue

第二个的颜色是 yellow, cyan, and magenta

第三个的颜色是 white, gray, and black

mTriangle1Vertices.put(triangle1VerticesData).position(0); 直接用把浮点数组放到FloatBuffer中

public void onSurfaceCreated(GL10 glUnused, EGLConfig config

  这里有一个绕不过去的坎,就是vertex attribute的概念. OpenGL2.0不是固定管线(什么是固定管线?), 允许用户自定义vetex的数据. 如何自定义呢,就要看vertex attribute. 它既可以是vertex array, 也可以是constant value ? 错了, attribute是指针对每个vertex的position, color, normal, texture coordinates这些属性. 这些属性数据可以放到constant value,也可以放到vertex arrays里面.

The use of constant value is infrequent. Because they will most likely be stored as single precision floating-point values internally, ES 2.0 only supports the float variant.

Vertex arrays specify attribute data per vertex and are buffers stored in the application’s address space (what OpenGL ES calls the client space).

Vertex arrays are specified using the glVertexAttribPointer function. 这里就引入了glVertexAttribPointer 函数, 这个函数用来说明vertex array在内存中的layout并传入vertex array.

这个函数准确的说是, 对vertex array存放的n个顶点的属性数据的内存布局进行细致的描述.

几个参数如下

  • index | specifies the generic vertex attribute index. This value is 0 to max vertex attributes supported – 1. 指定对于每个vertex的数据, 这个属性的index, 也就是说明属性间先后关系和顺序. 比如一般位置坐标放第0个位置, 接着放颜色到第1个位置, 然后是texture数据第2个位置. 一般最多放7个属性.

  • stride | specifies the delta between data for vertex index I and vertex (I + 1). 就是每个vertex index指向的数据到下个vertex index指向数据的间隔.(也可以理解为每个vertex的某个属性到下个vertex该属性数据的间隔) 比如vertex array的数据用来画一个三角形,就有三个顶点放在里面, 一个顶点有x,y,z,r,g,b,a七个float类型数据, stride就是4*7 = 28

  • size | number of components specified in the vertex array for the vertex attribute referenced by index. Valid values are 1–4. 这段有点拗口,其实就是说每个vertex attribute使用了几个原始数据(element),比如位置属性使用了x,y,z三个原始数据来存放, size=3

  • type | data format, 比如byte, short, float等, 指明该属性用的数据类型, 如果属性是位置坐标, 那么x,y,z就都是float

看下代码, 先指定了传给shader的位置属性,然后指定了其它属性

#define VERTEX_POS_INDX 0
#define VERTEX_POS_SIZE 3   // x, y and z
#define VERTEX_ATTRIB_SIZE VERTEX_POS_SIZE + \
                  VERTEX_NORMAL_SIZE + \
                  VERTEX_TEXCOORD0_SIZE + \
                    VERTEX_TEXCOORD1_SIZE

// position is vertex attribute 0
glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE,  GL_FLOAT, GL_FALSE, VERTEX_ATTRIB_SIZE * sizeof(float), p);

vertex array由指针p指定, 位置属性的index是0, size是3个elements, element的类型是float, 间隔是 10个float

// normal is vertex attribute 1
  glVertexAttribPointer(VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE, GL_FLOAT, GL_FALSE, VERTEX_ATTRIB_SIZE * sizeof(float), (p + VERTEX_NORMAL_OFFSET));


// texture coordinate 0 is vertex attribute 2
  glVertexAttribPointer(VERTEX_TEXCOORD0_INDX, VERTEX_TEXCOORD0_SIZE,GL_FLOAT, GL_FALSE, VERTEX_ATTRIB_SIZE * sizeof(float), (p + VERTEX_TEXCOORD0_OFFSET));


// texture coordinate 1 is vertex attribute 3
  glVertexAttribPointer(VERTEX_TEXCOORD1_INDX, VERTEX_TEXCOORD1_SIZE, GL_FLOAT, GL_FALSE, VERTEX_ATTRIB_SIZE * sizeof(float), (p + VERTEX_TEXCOORD1_OFFSET));    

现在回过头来看 drawTriangle里面的代码也是同样用法. 对于

GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, mStrideBytes, aTriangleBuffer);

  index是mPositionHandle, size是mPositionDataSize, stride是mStrideBytes, vetex array是aTriangleBuffer  

而 mPositionHandle 是由这段

mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");

得到的, 而不是一个预先定义的,这又是什么用法呢? 官方对glGetAttribLocation的解释是

glGetAttribLocation queries the previously linked program object specified by program for the attribute variable specified by name and returns the index of the generic vertex attribute that is bound to that attribute variable. If nameis a matrix attribute variable, the index of the first column of the matrix is returned. If the named attribute variable is not an active attribute in the specified program object or if name starts with the reserved prefix "gl_", a value of -1 is returned.
这段只说了
returns the index of the generic vertex attribute that is bound to that attribute variable.

没有说the index of the generic vertex attribute怎么来的,于是又引入了

glBindAttribLocation — associate a generic vertex attribute index with a named attribute variable

查一下例子的代码

    // Bind attributes
      GLES20.glBindAttribLocation(programHandle, 0, "a_Position"); 

看来还是预先定义了位置属性的"the index of the generic vertex attribute".
所以前面GLES20.glVertexAttribPointer拿到的mPositionHandle可能就是0了,可以跟下代码, confirm一下.

接下来就是对Matrix.setLookAtM的理解了.这个函数和gluLookAt比较相似(也有区别), 可以参考下面对gluLookAt的解释:将模型坐标系(model view)转换到世界坐标系(world view)后, 再把世界坐标系转换为照相机坐标系(camera view).
http://www.360doc.com/content/14/1028/10/19175681_420515511.shtml
https://blog.csdn.net/kkae8643150/article/details/52805738 对上面的文章多了一些例子
https://blog.csdn.net/qq_28615177/article/details/51524127 gluLookAt与setLookAtM的区别
https://blog.csdn.net/lyzirving/article/details/79022445 [Android OpenGL ES2.0 setLookAtM()方法] 用例子对setLookAtM的结果解释得很好, 不过没有解释原理
https://www.jianshu.com/p/0a4011d852d2?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

然后是对Matrix.setIndentity的理解,
https://gamedev.stackexchange.com/questions/65962/identity-matrix-what-does-it-really-do 没看明白
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/ 对矩阵讲得很好, 也有The Model, View and Projection matrices的内容,还么有看
https://stuff.mit.edu/afs/sipb/project/android/docs/reference/android/opengl/Matrix.html 这里有点想官方文档,虽然不是

public void onSurfaceChanged(GL10 glUnused, int width, int height)

Store the projection matrix. This is used to project the scene onto a 2d viewport.

投影和变换比较好的文章

https://juejin.im/post/59c7c8adf265da065a63cd3b 用心写的文章, 赞!
https://blog.csdn.net/tanmx219/article/details/81407264
https://blog.csdn.net/mzpmzk/article/details/64924017
https://blog.csdn.net/wangdingqiaoit/article/details/51589825 https://blog.csdn.net/wangdingqiaoit 博客
https://blog.csdn.net/qq_33426650/article/details/55043064
https://blog.csdn.net/pangrui201/article/details/75452155 对于正交投影说得不错,而且数学也有解释 https://blog.csdn.net/pangrui201/article/list/3 博客
https://blog.csdn.net/pangrui201/article/details/75540789

https://blog.csdn.net/hansion3333/article/details/79116755 只是对官方教材简单的说明一下,不过可以看看
https://blog.csdn.net/lyx2007825/article/details/8792475 偏数学了点
https://solarianprogrammer.com/2013/05/22/opengl-101-matrices-projection-view-model/
http://www.cnblogs.com/wubugui/p/4367592.html
http://www.learnopengles.com/understanding-opengls-matrices/
https://www.scratchapixel.com/index.php?redirect
https://www.3dgep.com/understanding-the-view-matrix/
https://blog.csdn.net/fanbird2008/article/details/78871767 离屏渲染

My practice

public void onDrawFrame(GL10 glUnused)

  • 三个三角形在同一个位置分别绕x, y, z axis旋转

      Matrix.setIdentityM(mModelMatrix, 0);
      Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 1.0f, 0.0f);
      drawTriangle(mTriangle1Vertices);
    
      Matrix.setIdentityM(mModelMatrix, 0);
      Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
      drawTriangle(mTriangle2Vertices);
    
    
      Matrix.setIdentityM(mModelMatrix, 0);
      Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 1.0f, 0.0f, 0.0f);
      drawTriangle(mTriangle3Vertices);
    

电脑上怎么截android的屏幕?
这里能换成3d形状的旋转吗?
这里能换成复杂的形状, 比如车模吗?
这里能有触摸或者鼠标键盘控制旋转的角度吗?
这里能像游戏一样切换视角吗?
这里能控制模型的移动并切换视角吗?

Reference

  • FloatBuffer

https://stackoverflow.com/questions/10697161/why-floatbuffer-instead-of-float
https://docs.oracle.com/javase/7/docs/api/java/nio/FloatBuffer.html

“ I've been using FloatBuffers in my Android code for a while (copied it from some opengles tutorial), but I cannot understand exactly what this construct is and why it is needed.”

“The reason for creating a ByteBuffer first is that you want to use the allocateDirect call to create a direct byte buffer, which benefits from the accelerated operations. You then create a FloatBuffer from this that shares the same memory. The FloatBuffer doesn't itself have an allocateDirect method for some reason, which is why you have to go via ByteBuffer.”

  • glVertexAttribPointer

https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glVertexAttribPointer.xhtml
https://stackoverflow.com/questions/17149728/when-should-glvertexattribpointer-be-called

  • glGetAttribLocation

https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetAttribLocation.xml

posted @ 2018-11-15 18:08  Saymour2008  阅读(763)  评论(0编辑  收藏  举报