阅读文章前需要了解的知识,你好,三角形:https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/

  要渲染出几何图形来,首先需要变换矩阵,那么自然就需要一个数学库了。本来想用 glm 库的,但用不惯这个库,只好编写一个简单的数据库了。这个库暂不需要复杂的功能,就几个向量类和 4x4 的矩阵类,矩阵类有一个重要的函数,用于创建正交矩阵(由于这是一个简单的项目,就不需要 LookAt(视图) 矩阵了)。

    Matrix4 Matrix4::ortho(GLfloat fLeft, GLfloat fRight, GLfloat fBottom, GLfloat fTop, GLfloat fNear, GLfloat fFar)
{
Matrix4 mat4 = Matrix4::ZERO; mat4.m[][] = / (fRight - fLeft);
mat4.m[][] = / (fTop - fBottom);
mat4.m[][] = / (fNear - fFar);
mat4.m[][] = ; mat4.m[][] = -(fRight + fLeft) / (fRight - fLeft);
mat4.m[][] = -(fTop + fBottom) / (fTop - fBottom);
mat4.m[][] = (fNear + fFar) / (fNear - fFar); return mat4;
}

对于这个矩阵的推导感兴趣的话,这里推荐一篇文章:http://blog.csdn.net/popy007/article/details/4126809

接着需要着色器程序,可以渲染几何图形就行了

const GLchar *shader_vs = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec4 color;\n"
"uniform mat4 projection;\n"
"out vec4 Vcolor;\n"
"void main(){\n"
"gl_Position = vec4(position, 1.0f);\n"
"Vcolor = color;\n"
"}"; const GLchar *shader_frag = "#version 330 core\n"
"out vec4 color;\n"
"in vec4 Vcolor;\n"
"void main(){\n"
"color = Vcolor;\n"
"}";

创建着色程序并绑定

    void GraphicsContext::createShaderProgram()
{
/* 创建顶点作色器 */
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, , &shader_vs, NULL);
glCompileShader(vertexShader); GLint success;
GLchar infoLog[];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if ( !success ) {
glGetShaderInfoLog(vertexShader, , NULL, infoLog);
return;
} /* 创建片段着色器 */
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, , &shader_frag, NULL);
glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if ( !success ) {
glGetShaderInfoLog(fragmentShader, , NULL, infoLog);
return;
} /* 创建着色程序 */
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram); glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if ( !success ) {
glGetProgramInfoLog(shaderProgram, , NULL, infoLog);
return;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader); /* 使用着色程序 */
glUseProgram(shaderProgram);
}

准备阶段已经完成,接下来开始渲染几何图形,新建一个渲染器类 Renderer,渲染顶点数据。此外,还有一个储存顶点数据的类

        class VertexData
{
public:
std::vector<Vec3> positions;
std::vector<Vec2> texcoords;
std::vector<Color> colors;
std::vector<GLuint> indices; int nPositionCount;
int nIndexCount;
bool bHasTexcoord; RenderType renderType; void clear()
{
nPositionCount = ;
nIndexCount = ;
} void resize(int positionCount, int indexCount)
{
if ( positions.size() - nPositionCount < positionCount ) {
positions.resize(positions.size() + positionCount);
colors.resize(colors.size() + positionCount); if ( bHasTexcoord ) texcoords.resize(texcoords.size() + positionCount);
}
if ( indices.size() - nIndexCount < indexCount ) {
indices.resize(indices.size() + indexCount);
}
} void pushData(const Vec3& pos, const Color& color)
{
positions[nPositionCount] = pos;
colors[nPositionCount++] = color;
} void pushData(const Vec3& pos, const Vec2& texcoord, const Color& color)
{
positions[nPositionCount] = pos;
texcoords[nPositionCount] = texcoord;
colors[nPositionCount++] = color;
} void pushIndex(GLuint index)
{
indices[nIndexCount++] = index;
}
};

主要储存顶点位置,顶点颜色,纹理坐标(现在先不用)和顶点的索引。

渲染类型

    enum RenderType
{
RENDER_TYPE_LINES,
RENDER_TYPE_TRIANGLES,
RENDER_TYPE_TEXTURE
};

渲染单元的结构

    struct RenderUnit
{
Vec3* pPositions;
int nPositionCount; Color* pColors;
bool bSameColor; GLuint* pIndices;
int nIndexCount; RenderType renderType;
};

渲染器头文件

#pragma once
#include "Common.h"
#include "Math.h" #include <vector> namespace Simple2D
{
enum RenderType
{
RENDER_TYPE_LINES,
RENDER_TYPE_TRIANGLES,
RENDER_TYPE_TEXTURE
}; struct RenderUnit
{
Vec3* pPositions;
int nPositionCount; Color* pColors;
bool bSameColor; GLuint* pIndices;
int nIndexCount; RenderType renderType;
}; class DLL_export Renderer
{
class VertexData
{
public:
std::vector<Vec3> positions;
std::vector<Vec2> texcoords;
std::vector<Color> colors;
std::vector<GLuint> indices; int nPositionCount;
int nIndexCount;
bool bHasTexcoord; RenderType renderType; void clear()
{
nPositionCount = ;
nIndexCount = ;
} void resize(int positionCount, int indexCount)
{
if ( positions.size() - nPositionCount < positionCount ) {
positions.resize(positions.size() + positionCount);
colors.resize(colors.size() + positionCount); if ( bHasTexcoord ) texcoords.resize(texcoords.size() + positionCount);
}
if ( indices.size() - nIndexCount < indexCount ) {
indices.resize(indices.size() + indexCount);
}
} void pushData(const Vec3& pos, const Color& color)
{
positions[nPositionCount] = pos;
colors[nPositionCount++] = color;
} void pushData(const Vec3& pos, const Vec2& texcoord, const Color& color)
{
positions[nPositionCount] = pos;
texcoords[nPositionCount] = texcoord;
colors[nPositionCount++] = color;
} void pushIndex(GLuint index)
{
indices[nIndexCount++] = index;
}
}; public:
Renderer();
~Renderer(); void render();
void renderVertexData(VertexData& vertexData); void pushRenderUnit(const RenderUnit& unit); private:
void initBuffers();
Vec3 tranformPosition(Vec3& pos); private:
VertexData triangleData;
VertexData lineData; GLuint positionBuffer;
GLuint colorBuffer;
GLuint indexBuffer;
GLuint VAO; Matrix4 mTransformMatrix;
};
}

triangleData,储存绘制三角形图元的顶点数据

lineData,储存绘制线段的顶点数据

RenderUnit类,通过 pushRenderUnit 函数, 将顶点数据传到渲染器,然后渲染器将相同渲染类型的 RenderUnit 数据储存到同一个顶点数据缓冲区(VertexData)。

    void Renderer::pushRenderUnit(const RenderUnit& unit)
{
VertexData* vertexData = nullptr;
if ( unit.renderType == RENDER_TYPE_TRIANGLES ) {
vertexData = &triangleData;
}
else if ( unit.renderType == RENDER_TYPE_LINES ) {
vertexData = &lineData;
} /* 填充数据 */
vertexData->resize(unit.nPositionCount, unit.nIndexCount); int baseIndex = vertexData->nPositionCount;
for ( int i = ; i < unit.nPositionCount; i++ ) {
if ( unit.bSameColor ) {
vertexData->pushData(tranformPosition(unit.pPositions[i]), unit.pColors[]);
}
else {
vertexData->pushData(tranformPosition(unit.pPositions[i]), unit.pColors[i]);
}
}
for ( int i = ; i < unit.nIndexCount; i++ ) {
vertexData->pushIndex(baseIndex + unit.pIndices[i]);
}
}

在函数中,对顶点位置向量进行了矩阵变换

    Vec3 Renderer::tranformPosition(Vec3& pos)
{
return mTransformMatrix * pos;
}

这个变换矩阵主要一个正交矩阵和一个变换矩阵组成

        Matrix4 ortho = Matrix4::ortho(, , , , -, );
Matrix4 tranform = Matrix4::makeTransform(Vec3(, , ), Vec3(, -, ));
mTransformMatrix = ortho * tranform;

因为坐标原点在左上角,并且 Y 轴朝下为正。所以通过一个变换矩阵 transform 变换到左下角,Y 轴朝上为正。

最后在函数 renderVertexData 中渲染出来

    void Renderer::renderVertexData(VertexData& vertexData)
{
/* 填充顶点数据 */
glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof( Vec3 ) * vertexData.nPositionCount, &vertexData.positions[], GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof( Color ) * vertexData.nPositionCount, &vertexData.colors[], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, ); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof( GLuint ) * vertexData.nIndexCount, &vertexData.indices[], GL_STATIC_DRAW);
glBindVertexArray(); glBindVertexArray(VAO);
switch ( vertexData.renderType ) {
case RENDER_TYPE_TRIANGLES:
glDrawElements(GL_TRIANGLES, vertexData.nIndexCount, GL_UNSIGNED_INT, );
break;
case RENDER_TYPE_LINES:
glDrawElements(GL_LINES, vertexData.nIndexCount, GL_UNSIGNED_INT, );
break;
}
glBindVertexArray(); vertexData.clear();
}

为了方便绘制几何图形,创建一个画布类 Canvas2D

#pragma once
#include "Common.h"
#include "Math.h" #include <vector> namespace Simple2D
{
class Renderer; class DLL_export Canvas2D
{
public:
Canvas2D(Renderer* renderer);
~Canvas2D(); void drawLine(int x1, int y1, int z1, int x2, int y2, int z2, Color& color); void drawCircle(const Vec3& center, int radius, Color& color, int nSlice = );
void fillCircle(const Vec3& center, int radius, int degrees, Color& color);
void fillCircle(const Vec3& center, int in_radius, int out_radius, int beginAngle, int endAngle, Color& color); void drawRect(float x, float y, float w, float h, Color color);
void fillRect(float x, float y, float w, float h, Color color); void drawTriangles(Vec3* positions, Color* colors, int positionCount, GLuint* indices, int indexCount, bool sameColor = true);
void drawLines(Vec3* positions, Color* colors, int positionCount, bool sameColor = true); private:
void resizeVector(int positionCount, int indexCount); Renderer* pRenderer; std::vector<Vec3> vPositions;
int nPositionCount; std::vector<GLuint> vIndices;
int nIndexCount;
};
}

如果要绘制一个矩形,需要准备顶点数据

    void Canvas2D::fillRect(float x, float y, float w, float h, Color color)
{
this->resizeVector(, ); vPositions[].set(x + , y + , );
vPositions[].set(x + , y + h, );
vPositions[].set(x + w, y + h, );
vPositions[].set(x + w, y + , ); vIndices[] = ;
vIndices[] = ;
vIndices[] = ;
vIndices[] = ;
vIndices[] = ;
vIndices[] = ; this->drawTriangles(&vPositions[], &color, , &vIndices[], );
}

然后在函数 drawTriangle 中将数据传递到 渲染器

    void Canvas2D::drawTriangles(Vec3* positions, Color* colors, int positionCount, GLuint* indices, int indexCount, bool sameColor)
{
static RenderUnit unit;
unit.pPositions = positions;
unit.nPositionCount = positionCount;
unit.pIndices = indices;
unit.nIndexCount = indexCount;
unit.pColors = colors;
unit.bSameColor = sameColor;
unit.renderType = RENDER_TYPE_TRIANGLES; pRenderer->pushRenderUnit(unit);
}

最后在主函数中绘制几个图形

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
RenderWindow window(DEFAULT_WIN_W, DEFAULT_WIN_H);
GraphicsContext graphicsContext(&window); Canvas2D canvas(graphicsContext.getRenderer()); MSG msg = { };
while ( msg.message != WM_QUIT ) {
if ( PeekMessage(&msg, , , , PM_REMOVE) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
float n = ;
for ( int i = ; i < n; i++ ) {
for ( int j = ; j < n; j++ ) {
if ( i % == && j % == ) {
canvas.fillRect(
+ i * , + j * , , ,
Color(i / n, i / n, , ));
}
else {
canvas.drawRect(
+ i * , + j * , , ,
Color(i / n, i / n, , ));
}
}
} canvas.drawLine(, , , , , , Color(, , , ));
canvas.drawCircle(Vec3(, , ), , Color(, , , )); canvas.fillCircle(Vec3(, , ), , , , , Color(, , , ));
canvas.fillCircle(Vec3(, , ), , , , , Color(, , , ));
canvas.fillCircle(Vec3(, , ), , , , , Color(, , , )); graphicsContext.flip();
}
}
return ;
}

运行程序的结果

源码地址:http://files.cnblogs.com/files/ForEmail5/Simple2D-03.rar

基于OpenGL编写一个简易的2D渲染框架-03 渲染基本几何图形的更多相关文章

  1. 基于OpenGL编写一个简易的2D渲染框架-05 渲染文本

    阅读文章前需要了解的知识:文本渲染 https://learnopengl-cn.github.io/06%20In%20Practice/02%20Text%20Rendering/ 简要步骤: 获 ...

  2. 基于OpenGL编写一个简易的2D渲染框架-06 编写一个粒子系统

    在这篇文章中,我将详细说明如何编写一个简易的粒子系统. 粒子系统可以模拟许多效果,下图便是这次的粒子系统的显示效果.为了方便演示,就弄成了一个动图. 图中,同时显示了 7 种不同粒子效果,看上去效果挺 ...

  3. 基于OpenGL编写一个简易的2D渲染框架-01 创建窗口

    最近正在学习OpenGL,我认为学习的最快方法就是做一个小项目了. 如果对OpenGL感兴趣的话,这里推荐一个很好的学习网站 https://learnopengl-cn.github.io/ 我用的 ...

  4. 基于OpenGL编写一个简易的2D渲染框架-04 绘制图片

    阅读文章前需要了解的知识,纹理:https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/ 过程简述:利用 FreeI ...

  5. 基于OpenGL编写一个简易的2D渲染框架-02 搭建OpenGL环境

    由于没有使用GLFW库,接下来得费一番功夫. 阅读这篇文章前请看一下这个网页:https://learnopengl-cn.github.io/01%20Getting%20started/02%20 ...

  6. 基于OpenGL编写一个简易的2D渲染框架-11 重构渲染器-Renderer

    假如要渲染一个纯色矩形在窗口上,应该怎么做? 先确定顶点的格式,一个顶点应该包含位置信息 vec3 以及颜色信息 vec4,所以顶点的结构体定义可以这样: struct Vertex { Vec3 p ...

  7. 基于OpenGL编写一个简易的2D渲染框架-08 重构渲染器-整体架构

    事实上,前面编写的渲染器 Renderer 非常简陋,虽然能够进行一些简单的渲染,但是它并不能满足我们的要求. 当渲染粒子系统时,需要开启混合模式,但渲染其他顶点时却不需要开启混合模式.所以同时渲染粒 ...

  8. 基于OpenGL编写一个简易的2D渲染框架-09 重构渲染器-Shader

    Shader 只是进行一些简单的封装,主要功能: 1.编译着色程序 2.绑定 Uniform 数据 3.根据着色程序的顶点属性传递顶点数据到 GPU 着色程序的编译 GLuint Shader::cr ...

  9. 基于OpenGL编写一个简易的2D渲染框架-10 重构渲染器-Pass

    Pass,渲染通路,一个渲染通路指的是一次像素处理和一次顶点处理,也就是指的是一次绘制.简单来说就是顶点数据在渲染管线中走一遍最后绘制. 渲染粒子系统的粒子时,需要开启 OpenGL 的混合模式,并使 ...

随机推荐

  1. Mac OSX 正确地同时安装Python 2.7 和Python3

    出处:http://www.jianshu.com/p/51811fa24752 python3 默认安装位置:/usr/local/Cellar/python3

  2. yarn 制作 npm 包

    yarn publish yarn info [package_name]

  3. PHP查找中文字符的解决方案

    在PHP中查找中文字符,有两种方案.1.中文字符是gbk(gb2312)有两种解决方法第一种:将PHP保存为ASCII编码,然后使用strpos查找,如:strpos($curl_res, ‘哈哈’) ...

  4. DKhadoop大数据系统架构设计方案

    大数据作为当下最为热门的事件之一,其实已经不算是很新鲜的事情了.如果是三五年前在讨论大数据,那可能会给人一种很新鲜的感觉.大数据作为当下最为重要的一项战略资源,已经是越来越得到国家和企业的高度重视,我 ...

  5. ASM配置管理

    http://blog.chinaunix.net/uid-22646981-id-3060280.htmlhttp://blog.sina.com.cn/s/blog_6a5aa0300102uys ...

  6. tomcat和servlet的关系

    一.什么是servlet? 处理请求和发送响应的过程是由一种叫做Servlet的程序来完成的,并且Servlet是为了解决实现动态页面而衍生的东西.理解这个的前提是了解一些http协议的东西,并且知道 ...

  7. linux 信号处理 五 (示例)

    [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执 ...

  8. C/C++基础----表达式

    1 基本概念 类型转换,小整型通常会被提升. 运算符重载,运算对象的个数.运算符的优先级和结合律都是无法改变的. 左值右值,对象被用做右值时,使用的是对象的值(内容):用做左值时,使用的是对象的身份( ...

  9. java-appium-527进阶-1 UiAutomator1&2区别和封装

    1.UiAutomator和UiAtumator2的区别: 1.1 UiAutomator1有关于id定位的策略 UiAutomator1 id定位在resourceid匹配失败时,会匹配conten ...

  10. oracle11g的dmp文件导入oracle10g时报错:头部验证失败

    因为本机安装的10g版本的Oracle,需要导入一个11g版本导出的dmp文件,Oracle数据库版本之间存在兼容的问题,低版本的库不能导入高版本的dmp文件,在CMD中导入dmp文件总是出现如下错误 ...