本系列教程主要讲解利用Python和OpenGL开发三维图形程序。这里主要用到的工具库是glfw开源图形库。本系列内容较难。要求学生对几何和编程有一定的了解。建议初三以上同学学习。
目录
曲面细分着色器,就是在一个基础图形的基础上,按照一定规律重复递归细分。这样做有两个好处,1.模型数据加载不用再那么庞大。把细分工作放到渲染阶段。而且这个细分过程是可控。2.由于细分过程是在GPU上完成,所以速度比CPU上细分快得多。
曲面细分过程分为两个过程。1.曲面细分控制着色器(Tessellation Control Shaders)。2.曲面细分求值着色器(Tessellation Evaluation Shaders)。前者负责细分的精细度和规律,后者实现细分过程。
Tessellation Control Shader的核心就是gl_TessLevelOuter和gl_TessLevelInner,前者决定外围的细分等级,后者决定内部的细分等级。其数值代表将该段分为几部分,即如果值为2,那么就是在中间插入1个点分为两段。如下图:
下面我在上节渲染三角形基础上,改写一个细分的例子。
triangle.vs顶点着色文件
#version 430
layout (location = 0) in vec2 in_Pos;
layout (location = 1) in vec3 in_Color;
out vec4 out_Pos;
out vec3 out_Color;
void main()
{
gl_Position = vec4(in_Pos.x, in_Pos.y, 0, 1.0);
out_Pos = vec4(in_Pos.x, in_Pos.y, 0, 1.0);
out_Color = in_Color;
}
triangle.cs曲面细分控制着色文件
#version 450 core
layout (vertices = 3) out;
in vec4 out_Pos[];
in vec3 out_Color[];
out vec4 tcs_pos[];
out vec3 tcs_color[];
void main(){
//inner
gl_TessLevelInner[0] = 5;
// outer
gl_TessLevelOuter[0] = 5;
gl_TessLevelOuter[1] = 5;
gl_TessLevelOuter[2] = 5;
tcs_pos[gl_InvocationID] = out_Pos[gl_InvocationID];
tcs_color[gl_InvocationID] = out_Color[gl_InvocationID];
}
triangle.es曲面细分求值着色文件
#version 450 core
layout(triangles, equal_spacing,ccw) in;
vec3 interpolate3D(vec3 v0, vec3 v1, vec3 v2)
{
return vec3(gl_TessCoord.x) * v0 + vec3(gl_TessCoord.y) * v1 + vec3(gl_TessCoord.z) * v2;
}
in vec4 tcs_pos[];
in vec3 tcs_color[];
out vec3 out_Color;
void main(){
vec3 iterP = interpolate3D(tcs_pos[0].xyz,tcs_pos[1].xyz,tcs_pos[2].xyz);
vec3 iterCd = interpolate3D(tcs_color[0],tcs_color[1],tcs_color[2]);
out_Color = iterCd;
gl_Position = vec4(iterP , 1);
}
triangle.fs片元着色文件
#version 450 core
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) #将着色器对象关联到程序上
return ShaderObj
#=======================================
#编译着色器
def Compile_Shader():
vs_file = get_file("triangle.vs")
cs_file = get_file("triangle.cs")
es_file = get_file("triangle.es")
fs_file = get_file("triangle.fs")
Shader_Program = glCreateProgram() #创建空的着色器程序
vs_shader = Create_Shader(Shader_Program , GL_VERTEX_SHADER , vs_file)
fs_shader = Create_Shader(Shader_Program , GL_FRAGMENT_SHADER , fs_file)
cs_shader = Create_Shader(Shader_Program , GL_TESS_CONTROL_SHADER , cs_file)
es_shader = Create_Shader(Shader_Program , GL_TESS_EVALUATION_SHADER , es_file)
glLinkProgram(Shader_Program)
glUseProgram(Shader_Program)
glDeleteShader(vs_shader)
glDeleteShader(cs_shader)
glDeleteShader(es_shader)
glDeleteShader(fs_shader)
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")
#创建缓存
VBO = glGenBuffers(2)
#绑定缓冲区
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)
#=======================================
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()
print(glGetString(GL_VERSION))
print(glGetString(GL_VENDOR))
# 窗口事件循环
while not glfw.window_should_close(window):
glClear(GL_COLOR_BUFFER_BIT)
glClearColor(0,0,0,1)
glPolygonMode(GL_FRONT_AND_BACK ,GL_LINE );
glPatchParameteri(GL_PATCH_VERTICES,3);
#绑定顶点缓冲区
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, VBO[0])
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, None)
#绑定颜色缓冲区
glEnableVertexAttribArray(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO[1])
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, None)
#引用Shader
glUseProgram(Shader_Program)
#绘制镶嵌三角形
glDrawArrays(GL_PATCHES, 0, 3)
#恢复禁止通道
glDisableVertexAttribArray(0)
glDisableVertexAttribArray(1)
# 交换缓冲区,提交渲染内容
glfw.swap_buffers(window)
# 窗口事件轮询
glfw.poll_events()
glfw.terminate()