Vulkan

II. Positioning---Chapter 3----Moving the Vertices

Chapter 3. OpenGL's Moving Triangle


This tutorial is about how to move objects around. It will introduce new shader techniques.

Moving the Vertices

The simplest way one might think to move a triangle or other object around is to simply modify the vertex position data directly. From previous tutorials, we learned that the vertex data is stored in a buffer object. So the task is to modify the vertex data in the buffer object. This is whatcpuPositionOffset.cpp does.

The modifications are done in two steps. The first step is to generate the X, Y offset that will be applied to each position. The second is to apply that offset to each vertex position. The generation of the offset is done with the ComputePositionOffset function:

修改buffer object中的顶点数据分两个步骤.第一步骤是产生施加到顶点的X,Y方向的offsets .第二步将offsets 作用于每个顶点.函数如下:

Example 3.1. Computation of Position Offsets

void ComputePositionOffsets(float &fXOffset, float &fYOffset)
{
    const float fLoopDuration = 5.0f;
    const float fScale = 3.14159f * 2.0f / fLoopDuration;
    
    float fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
    
    float fCurrTimeThroughLoop = fmodf(fElapsedTime, fLoopDuration);
    
    fXOffset = cosf(fCurrTimeThroughLoop * fScale) * 0.5f;
    fYOffset = sinf(fCurrTimeThroughLoop * fScale) * 0.5f;
}

This function computes offsets in a loop. The offsets produce circular motion, and the offsets will reach the beginning of the circle every 5 seconds (controlled by fLoopDuration). The function glutGet(GLUT_ELAPSED_TIME) retrieves the integer time in milliseconds since the application started. The fmodf function computes the floating-point modulus of the time. In lay terms, it takes the first parameter and returns the remainder of the division between that and the second parameter. Thus, it returns a value on the range [0, fLoopDuration), which is what we need to create a periodically repeating pattern.

函数在循环中计算offsets .offsets 的结果是一个圆环运动,offsets 会在每5秒到达圆环起点(由 fLoopDuration控制).函数glutGet(GLUT_ELAPSED_TIME) 在程序开始时以毫秒为单位获得整数时间. fmodf ()函数返回时间的浮点数模运算结果.结果为(第一个参数)%(第二个参数).这样它的返回值的范围为 [0, fLoopDuration),正好是我们需要创建的周期反复的方式.

The cosf and sinf functions compute the cosine and sine respectively. It is not important to know exactly how these functions work, but they effectively compute a circle of diameter 2. By multiplying by 0.5f, it shrinks the circle down to a circle with a diameter of 1.

Once the offsets are computed, the offsets have to be added to the vertex data. This is done with the AdjustVertexData function:

计算好offsets后,下面的函数是将其add到顶点数据. AdjustVertexData实现如下:

Example 3.2. Adjusting the Vertex Data

void AdjustVertexData(float fXOffset, float fYOffset)
{
    std::vector<float> fNewData(ARRAY_COUNT(vertexPositions));
    memcpy(&fNewData[0], vertexPositions, sizeof(vertexPositions));
    
    for(int iVertex = 0; iVertex < ARRAY_COUNT(vertexPositions); iVertex += 4)
    {
        fNewData[iVertex] += fXOffset;
        fNewData[iVertex + 1] += fYOffset;
    }
    
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertexPositions), &fNewData[0]);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

This function works by copying the vertex data into a std::vector, then applying the offset to the X and Y coordinates of each vertex. The last three lines are the OpenGL-relevant parts.

这个函数是将顶点数据复制到一个std::vector中,然后将offsets作用在顶点的X,Y坐标上.最后三行是OpenGL相关部分.

First, the buffer objects containing the positions is bound to the context. Then the new functionglBuffersSubData() is called to transfer this data to the buffer object.

首先,buffer objects包含绑定到context的顶点位置数据.然后调用新函数glBuffersSubData()将这个数据传送到buffer object. 

The difference between glBufferData and glBufferSubData is that the SubData function does not allocate memory. glBufferData specifically allocates memory of a certain size; glBufferSubData only transfers data to the already existing memory. CallingglBufferData on a buffer object that has already been allocated tells OpenGL to reallocate this memory, throwing away the previous data and allocating a fresh block of memory. Whereas calling glBufferSubData on a buffer object that has not yet had memory allocated byglBufferData is an error.

 glBufferData()函数和glBufferSubData()函数的区别是后者不 allocate 内存(分配内存?). glBufferData()按确定大小分配内存,glBufferSubData()只是把数据传送到已经存在的内存区域.调用glBufferData ()函数作用于一个已经分配好内存的buffer object 告诉OpenGL去重新分配此内存,删除前面的数据然后开辟新的内存块.而调用glBufferSubData()作用在一个没有使用glBufferData()分配内存的buffer object将是个错误.

Think of glBufferData as a combination of malloc and memcpy, while glBufferSubData is just memcpy.

可以将 glBufferData()看做是molloc和memcpy的结合,而glBufferSubData()只是memcpy.

The glBufferSubData function can update only a portion of the buffer object's memory. The second parameter to the function is the byte offset into the buffer object to begin copying to, and the third parameter is the number of bytes to copy. The fourth parameter is our array of bytes to be copied into that location of the buffer object.

glBufferSubData()函数可以只更新buffer object的部分数据.第二个参数是是buffer object需要更新的起始位置,第三个参数是需要更新的数据的size,第四个参数是需要复制到buffer object中的数据的指针.

The last line of the function is simply unbinding the buffer object. It is not strictly necessary, but it is good form to clean up binds after making them.

Buffer Object Usage Hints. Every time we draw something, we are changing the buffer object's data. OpenGL has a way to tell it that you will be doing something like this, and it is the purpose of the last parameter of glBufferData. This tutorial changed the allocation of the buffer object slightly, replacing:

Buffer Object Usage Hints.每次我们进行绘制,我们都是在改变buffer object的数据.有一个办法将这样的行为通知OpenGL,就是glBufferData()第四个参数的作用.我们要使用GL_STREAM_DRAW替换前文的GL_STATIC_DRAW.

glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);

with this:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STREAM_DRAW);

GL_STATIC_DRAW tells OpenGL that you intend to only set the data in this buffer object once. GL_STREAM_DRAW tells OpenGL that you intend to set this data constantly, generally once per frame. These parameters do not mean anything with regard to the API; they are simply hints to the OpenGL implementation. Proper use of these hints can be crucial for getting good buffer object performance when making frequent changes. We will see more of these hints later.

GL_STATIC_DRAW 告诉OpenGL对buffer object的数据你将会只set一次. GL_STREAM_DRAW 告诉OpenGL你会经常set这部分数据.这些参数对API无意义,只hintsOpenGL的实现部分.合理使用这些hints决定你是否能在频繁修改buffer object数据时候获得较好性能.

The rendering function now has become this:

Example 3.3. Updating and Drawing the Vertex Data

void display()
{
    float fXOffset = 0.0f, fYOffset = 0.0f;
    ComputePositionOffsets(fXOffset, fYOffset);
    AdjustVertexData(fXOffset, fYOffset);
    
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glUseProgram(theProgram);
    
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    glDisableVertexAttribArray(0);
    glUseProgram(0);
    
    glutSwapBuffers();
    glutPostRedisplay();
}

The first three lines get the offset and set the vertex data. Everything but the last line is unchanged from the first tutorial. The last line of the function is there to tell FreeGLUT to constantly call display. Ordinarily, display would only be called when the window's size changes or when the window is uncovered. glutPostRedisplay causes FreeGLUT to call display again. Not immediately, but reasonably fast.

前面三行获得offset并set顶点数据.最后一行是新增加的部分.glutPostRedisplay()的功能是告诉FreeGLUT不断的调用display()函数.通常当窗口大小改变或者被覆盖时不会调用display().glutPostRedisplay()引起FreeGLUT再次调用display().不是立即,但是足够快.

If you run the tutorial, you will see a smaller triangle (the size was reduced in this tutorial) that slides around in a circle.

posted on 2012-11-18 19:39  Vulkan  阅读(182)  评论(0编辑  收藏  举报

导航