图形的渲染显示
- 以正方形为例:正方形的绘制,与三角形的绘制类似。基于三角形的绘制,只需要将绘制三角形的部分代码修改,即可实现正方形的绘制;(三角形的绘制见上篇文章:OpenGL简单渲染一个三角形)
- 定义正方形顶点到原点的距离,即正方形的边长 = blockSize * 2;
// 图形顶点到原点的距离
GLfloat blockSize = 0.1f;
- 修改定点数组,并设置图元的连接方式和顶点个数;
// 设置正方形顶点,其中数组vVerts包含所有4个顶点的x,y,z笛卡尔坐标对
GLfloat vVerts[] = {-blockSize, -blockSize, 0.0f,
blockSize, -blockSize, 0.0f,
blockSize, blockSize, 0.0f,
-blockSize, blockSize, 0.0f};
// 批次处理,将数据传递到着⾊色器器
triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
- 至此,一个完整的正方形就绘制完成了。附上完整的正方形绘制代码:
#include "GLShaderManager.h"
#include "GLTools.h"
#include <GLUT/GLUT.h>
// 着色器
GLShaderManager shadermanager;
// 批次容器
GLBatch triangleBatch;
// 正方形的边长
GLfloat blockSize = 0.1f;
/// 窗口大小改变时接受新的宽度和高度
/// 触发条件: 1.新建窗口 2.窗⼝尺⼨发生调整
/// 处理理业务: 1.设置OpenGL 视⼝ 2. 设置OpenGL 投影⽅式等
/// @param w 像素
/// @param h 像素
void changeWindowSize(int w, int h) {
glViewport(0, 0, w, h);
}
void setupRC() {
glClearColor(0.90f, 0.40f, 0.0f, 1.0f);
shadermanager.InitializeStockShaders();
GLfloat vVerts[] = {-blockSize, -blockSize, 0.0f,
blockSize, -blockSize, 0.0f,
blockSize, blockSize, 0.0f,
-blockSize, blockSize, 0.0f};
triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
}
void renderScene(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat colorRed[] = {1.0f, 0.0f, 0.0f, 1.0f};
shadermanager.UseStockShader(GLT_SHADER_IDENTITY, colorRed);
triangleBatch.Draw();
glutSwapBuffers();
}
int main(int argc, char *argv[]) {
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(400, 400);
glutCreateWindow("正方形的移动");
glutReshapeFunc(changeWindowSize);
glutDisplayFunc(renderScene);
GLenum statue = glewInit();
if (GLEW_OK != statue) {
fprintf(stderr,"glew error:%s\n",glewGetErrorString(statue));
return 1;
}
setupRC();
glutMainLoop();
return 0;
}
图形的移动控制
- 在main函数中需要注册特殊键位响应,在specialKeys函数中实现移动的逻辑;
// 特殊键位触发(方向键)
glutSpecialFunc(specialKeys);
- 实现方式:坐标更新和矩阵变换
以正方形的初始位置和顶点的坐标如图为例;
- 坐标更新:以上图中的D点为基础,实现specialKeys的响应事件(通过以下代码即可改变的正方形顶点位置更新,从而控制整个正方形的初步移动效果);
/// 特殊键位的触发
/// @param key 键位key值
/// @param x 像素
/// @param y 像素
void specialKeys(int key, int x, int y) {
// 移动的步长
GLfloat setpSize = 0.05f;
// 需要移动点的偏移X、偏移Y值
GLfloat blockX = vVerts[0];
GLfloat blockY = vVerts[10];
// 方向键控制D点移动
if (key == GLUT_KEY_UP) {
blockY += setpSize;
}
if (key == GLUT_KEY_DOWN) {
blockY -= setpSize;
}
if (key == GLUT_KEY_LEFT) {
blockX -= setpSize;
}
if (key == GLUT_KEY_RIGHT) {
blockX += setpSize;
}
// 方向键控制A点移动
vVerts[0] = blockX;
vVerts[1] = blockY - blockSize * 2;
// 方向键控制B点移动
vVerts[3] = blockX + blockSize * 2;
vVerts[4] = blockY - blockSize * 2;
// 方向键控制C点移动
vVerts[6] = blockX + blockSize * 2;
vVerts[7] = blockY;
// 方向键控制D点移动
vVerts[9] = blockX;
vVerts[10] = blockY;
// 将顶点数组通过GLBatch帮助类将顶点传输到存储着⾊色器中,并手动出发渲染函数
triangleBatch.CopyVertexData3f(vVerts);
// 手动触发重新渲染
glutPostRedisplay();
}
- 不难发现,以上的代码存在一个问题:正方形可以无限移动,甚至可以移动出屏幕的范围,到了不可见的区域,这怎么办呢?其实很简单,只需要做“边缘检测”的处理,如下图:
具体代码的逻辑该怎么实现呢?只需要在setpSize的改变时加上正方形的大小即可,所以增加specialKeys方法中的部分逻辑代码;
// 判断正方形移动时的边界检测
if (blockX < -1.0f) {
blockX = - 1.0f;
}
if (blockX > 1.0f - blockSize * 2) {
blockX = 1.0f - blockSize * 2;
}
if (blockY < -1.0f + blockSize * 2) {
blockY = -1.0f + blockSize * 2;
}
if (blockY > 1.0f) {
blockY = 1.0f;
}
-
矩阵变换:以上的坐标变换方式只能针对顶点较少、图形简单的移动变换,当图形变得复杂,或者顶点特别多的时候,这样的处理就显得特别不实用,这就需要用到矩阵的方式进行变换(用矩阵记录移动的点,进行整体变换);
-
定义全局变量:
// x平移距离
GLfloat xPos = 0.0f;
// y平移距离
GLfloat yPos = 0.0f;
- 修改specialKeys方法中的逻辑;
// 矩阵变换
/// 特殊键位的触发
/// @param key 键位key值
/// @param x 像素
/// @param y 像素
void specialKeys(int key, int x, int y) {
// 移动的步长
GLfloat setpSize = 0.05f;
// 方向键控制D点移动
if (key == GLUT_KEY_UP) {
yPos += setpSize;
}
if (key == GLUT_KEY_DOWN) {
yPos -= setpSize;
}
if (key == GLUT_KEY_LEFT) {
xPos -= setpSize;
}
if (key == GLUT_KEY_RIGHT) {
xPos += setpSize;
}
// 碰撞检测
if (xPos < (-1.0 + blockSize)) {
xPos = -1.0 + blockSize;
}
if (xPos > (1.0 - blockSize)) {
xPos = 1.0 - blockSize;
}
if (yPos < (-1.0f + blockSize)) {
yPos = -1.0f + blockSize;
}
if (yPos > (1.0f - blockSize)) {
yPos = 1.0f - blockSize;
}
// 手动触发重新渲染
glutPostRedisplay();
}
- 修改renderScene方法:
// 矩阵变换
/// 开始渲染(触发条件:1.系统自动触发 2.⼿动调用函数触发)
void renderScene(void) {
// 清除一个或一组特定的缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat colorRed[] = {1.0f, 0.0f, 0.0f, 1.0f};
// 根据平移距离计算平移矩阵
M3DMatrix44f mTransFromMatrix;
m3dTranslationMatrix44(mTransFromMatrix, xPos, yPos, 0.0f);
// 将矩阵结果交给存储着色器(平面着色器)中绘制
shadermanager.UseStockShader(GLT_SHADER_FLAT, mTransFromMatrix, colorRed);
// 提交着色器
triangleBatch.Draw();
// 将在后台缓冲区进行渲染,然后在结束时交换到前台
glutSwapBuffers();
}