基于Python的OpenGL 03 之纹理

1. 概述

本文基于Python语言,描述OpenGL的着色器

前置知识可参考:

笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:

2. 纹理使用流程

参考:纹理 - LearnOpenGL CN (learnopengl-cn.github.io)

OpenGL中纹理使用流程大致如下:

  • 加载图片数据
  • 创建纹理对象
  • 绑定纹理对象
  • 使用图片数据生成纹理
  • 设置纹理坐标
  • 在顶点着色器中传递纹理
  • 在片段着色器中采用纹理
  • (绘制时)激活纹理并绑定纹理

3. 具体流程

3.1 加载图片数据

使用PIL(Pillow )实现图片数据的读取

参考官方说明:Image Module - Pillow (PIL Fork) 9.2.0 documentation

使用以下方式导入:

from PIL.Image import open

加载图片数据:

image = open('./textures/container.jpg')

3.2 创建纹理对象

纹理对象也是使用ID进行引用:

texture = glGenTextures(1)

3.3 绑定纹理对象

绑定纹理对象,进行之后的纹理配置:

glBindTexture(GL_TEXTURE_2D, texture)

进行纹理配置:

// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

3.4 使用图片数据生成纹理

通过glTexImage2D()生成纹理:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.size[0], image.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, image.tobytes())
glGenerateMipmap(GL_TEXTURE_2D)

3.5 设置纹理坐标

纹理坐标:

vertices = np.array([
     # ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
     0.5,  0.5, 0.0,   1.0, 0.0, 0.0,   1.0, 1.0,   # 右上
     0.5, -0.5, 0.0,   0.0, 1.0, 0.0,   1.0, 0.0,   # 右下
    -0.5, -0.5, 0.0,   0.0, 0.0, 1.0,   0.0, 0.0,   # 左下
    -0.5,  0.5, 0.0,   1.0, 1.0, 0.0,   0.0, 1.0    # 左上
                    ])

现在内存中的坐标格式:

img

指定纹理坐标属性:

glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, int(8 * 8), ctypes.c_void_p(8 * 6));
glEnableVertexAttribArray(2);

3.6 在顶点着色器中传递纹理坐标

在顶点着色器中编写GLSL实现数据传递:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
}

3.7 在片段着色器中采用纹理

在片段着色器中接收纹理坐标与纹理:

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D ourTexture;

void main()
{
    FragColor = texture(ourTexture, TexCoord);
}

使用GLSL内置的texture函数来采样纹理的颜色

3.8 激活纹理并绑定纹理(绘制时)

激活纹理单元并绑定纹理数据:

glActiveTexture(GL_TEXTURE0)  # 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);

4. 代码总结

一个简单的纹理绘制流程完整代码如下:

import glfw as glfw
from OpenGL.GL import *
import numpy as np
from PIL.Image import open

import shader as shader

glfw.init()
window = glfw.create_window(800, 600, "texture", None, None)
glfw.make_context_current(window)

VAO = glGenVertexArrays(1)
glBindVertexArray(VAO)

vertices = np.array([
     # ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
     0.5,  0.5, 0.0,   1.0, 0.0, 0.0,   1.0, 1.0,   # 右上
     0.5, -0.5, 0.0,   0.0, 1.0, 0.0,   1.0, 0.0,   # 右下
    -0.5, -0.5, 0.0,   0.0, 0.0, 1.0,   0.0, 0.0,   # 左下
    -0.5,  0.5, 0.0,   1.0, 1.0, 0.0,   0.0, 1.0,   # 左上
                    ])
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 8 * vertices.size, vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, int(8 * 8), None)
glEnableVertexArrayAttrib(VAO, 0)
glVertexAttribPointer(1, 3, GL_DOUBLE, GL_FALSE, int(8 * 8), ctypes.c_void_p(8 * 3))
glEnableVertexArrayAttrib(VAO, 1)
glVertexAttribPointer(2, 2, GL_DOUBLE, GL_FALSE, int(8 * 8), ctypes.c_void_p(8 * 6));
glEnableVertexAttribArray(2);

indices = np.array([
        0, 1, 3,  # first triangle
        1, 2, 3  # second triangle
        ])
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 8 * indices.size, indices, GL_STATIC_DRAW)

image = open('./textures/container.jpg')
# print(image.tobytes())
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
# 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.size[0], image.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, image.tobytes())
glGenerateMipmap(GL_TEXTURE_2D)

shaderProgram = shader.Shader("./glsl/test.vs.glsl", "./glsl/test.fs.glsl")

while not glfw.window_should_close(window):
    glClearColor(0.2, 0.3, 0.3, 1.0)
    glClear(GL_COLOR_BUFFER_BIT)

    shaderProgram.use()
    glBindVertexArray(VAO)
    glActiveTexture(GL_TEXTURE0)  # 在绑定纹理之前先激活纹理单元
    glBindTexture(GL_TEXTURE_2D, texture)

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)

    glfw.swap_buffers(window)
    glfw.poll_events()

shaderProgram.delete()

顶点着色器test.vs.glsl

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
}

片段着色器test.fs.glsl

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D texture1;

void main()
{
    FragColor = texture(texture1, TexCoord);
}

编译代码并运行:

image-20220729101233515

5. 参考资料

[1]纹理 - LearnOpenGL CN (learnopengl-cn.github.io)

[2]nothings/stb: stb single-file public domain libraries for C/C++ (github.com)

[3]OpenGL学习笔记(五)纹理 - 知乎 (zhihu.com)

[4]PyOpenGL 3.1.0 Function Reference (sourceforge.net)

[5]~mcfletch/openglcontext/trunk : contents of tests/dek_texturesurf.py at revision 699 (launchpad.net)

[6]Image Module - Pillow (PIL Fork) 9.2.0 documentation

posted @ 2022-08-07 22:11  当时明月在曾照彩云归  阅读(552)  评论(0编辑  收藏  举报