本系列教程主要讲解利用Python和OpenGL开发三维图形程序。这里主要用到的工具库是glfw开源图形库。本系列内容较难。要求学生对几何和编程有一定的了解。建议初三以上同学学习。
目录
当我们在一个场景中绘制多个相同物体时。比如绘制多棵树、草。大家想到的肯定是用一个for语句来重复绘制命令来实现。这种方法效率是非常低的。小场景还好,在大场景采用这种方法效率会非常低下。
效率低下原因是,for语句是在cpu下执行的。我要绘制100棵树,CPU就要提交100次数据给GPU,效率可想而知。那我们一次把100棵树提交给GPU可行吗?可以是可以,但是你的数据模型就会相当复杂,且庞大。
在OpenGL里提供了两个命令来解决此类,批量绘制相同物体的命令:glDrawArraysInstanced和glDrawElementsInstanced,它们分别对应glDrawArrays或glDrawElements函数。
下面例子是这样的,我要在屏幕绘制5个三角形,每个三角形的坐标都不一样。大家看下程序。
triangle.vs顶点着色文件
#version 330
layout (location = 0) in vec2 in_Pos;
layout (location = 1) in vec3 in_Color;
layout (location = 2) in vec2 in_offset;
out vec3 out_Color;
void main()
{
gl_Position = vec4(in_Pos.x*0.1+in_offset.x, in_Pos.y*0.1+in_offset.y, 0, 1.0);
out_Color = in_Color;
}
triangle.fs文件
#version 330
out vec4 FragColor;
in vec3 out_Color;
void main()
{
FragColor = vec4(out_Color,1);
}
main.py主文件
import glfw
import numpy as np
from OpenGL.GLUT import *
from OpenGL.GL import *
#获取文件内容
def get_file(fname):
tmp = open(fname,"r")
ret = tmp.read()
tmp.close()
return ret
#=======================================
#创建着色器
def Create_Shader( ShaderProgram, Shader_Type , Source):
#创建并且添加着色器
ShaderObj = glCreateShader( Shader_Type ) #创建Shader对象
glShaderSource(ShaderObj , Source)
glCompileShader(ShaderObj) #进行编译
glAttachShader(ShaderProgram, ShaderObj) #将着色器对象关联到程序上
#=======================================
#编译着色器
def Compile_Shader():
vs_file = get_file("triangle.vs")
fs_file = get_file("triangle.fs")
Shader_Program = glCreateProgram() #创建空的着色器程序
Create_Shader(Shader_Program , GL_VERTEX_SHADER , vs_file)
Create_Shader(Shader_Program , GL_FRAGMENT_SHADER , fs_file)
glLinkProgram(Shader_Program)
glUseProgram(Shader_Program)
return Shader_Program
#=======================================
def CreateBuffer(): #创建顶点缓存器
global VBO #设置为全局变量
#创建顶点数组
vertex = np.array([[-1.0,-1.0],
[1.0,-1.0],
[0.0,1.0],],dtype="float32")
#创建顶点颜色
color = np.array([[1,0,0],
[0,1,0],
[0,0,1],],dtype="float32")
#创建偏移数组
offsets = np.array([[-0.5,0.25],
[-0.25,-0.25],
[0,0],
[0.25,-0.25],
[0.5,0.25]],dtype="float32")
#创建缓存
VBO = glGenBuffers(3)
#绑定缓冲区
glBindBuffer(GL_ARRAY_BUFFER , VBO[0])
#输入数据
glBufferData(GL_ARRAY_BUFFER , vertex.nbytes , vertex , GL_STATIC_DRAW)
#绑定缓冲区
glBindBuffer(GL_ARRAY_BUFFER , VBO[1])
#输入数据
glBufferData(GL_ARRAY_BUFFER , color.nbytes , color , GL_STATIC_DRAW)
#绑定缓冲区
glBindBuffer(GL_ARRAY_BUFFER , VBO[2])
#输入数据
glBufferData(GL_ARRAY_BUFFER , offsets.nbytes , offsets , GL_STATIC_DRAW)
print(offsets.nbytes)
#=======================================
if __name__ == '__main__':
# 初始化GLFW
glfw.init()
# 创建窗口
window = glfw.create_window(640, 480, "实例化绘制", None, None)
if not window:
glfw.terminate()
#生成窗口上下文设备
glfw.make_context_current(window)
CreateBuffer()
Shader_Program = Compile_Shader()
# 窗口事件循环
while not glfw.window_should_close(window):
glClear(GL_COLOR_BUFFER_BIT)
glClearColor(0,0,0,1)
#绑定顶点缓冲区
glBindBuffer(GL_ARRAY_BUFFER, VBO[0])
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, None) #这里的None不能写为0
glEnableVertexAttribArray(0)
#绑定颜色缓冲区
glBindBuffer(GL_ARRAY_BUFFER, VBO[1])
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, None) #这里的None不能写为0
glEnableVertexAttribArray(1)
#绑定偏移缓冲区
glBindBuffer(GL_ARRAY_BUFFER, VBO[2])
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, None) #这里的None不能写为0
glEnableVertexAttribArray(2)
glVertexAttribDivisor(2, 1);
#glVertexAttribDivisor第二个参数含义:
#0:默认情况下是0,告诉OpenGL我们需要在顶点着色器的每次迭代时更新顶点属性。
#将它设置为1时,我们告诉OpenGL我们希望在渲染一个新实例的时候更新顶点属性。
#设置为2时,我们希望每2个实例更新一次属性,以此类推。
glLineWidth(10);
glUseProgram(Shader_Program)
#绘制三角形
glDrawArraysInstanced(GL_TRIANGLES,0,3,5)
#恢复禁止通道
glDisableVertexAttribArray(0)
glDisableVertexAttribArray(1)
# 交换缓冲区,提交渲染内容
glfw.swap_buffers(window)
# 窗口事件轮询
glfw.poll_events()
glfw.terminate()