一、目的
1、画一个四边形并加载纹理。
二、程序运行结果
三、纹理
纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节。
为了能够把纹理映射(Map)到四边形上,我们需要指定四边形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。
纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。
一般情况下,纹理图片是上下颠倒的,这是因为OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部。我们可以改变顶点数据的纹理坐标,翻转y值(用1减去y坐标)。
四、glTexImage2D解析
纹理绑定后,我们可以使用前面载入的图片数据生成一个纹理了。载入纹理的函数见参考文献2.
纹理可以通过glTexImage2D来生成:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值。
第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
下个参数应该总是被设为0(历史遗留问题)。
第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
最后一个参数是真正的图像数据。
当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。
五、源代码
"""
glfw_square05.py.py
Author: dalong10
Description: Draw a texture square, learning OPENGL
"""
import glutils #Common OpenGL utilities,see glutils.py
import sys, random, math
import OpenGL
from OpenGL.GL import *
from OpenGL.GL.shaders import *
import numpy
import numpy as np
import glfw
from PIL import Image
strVS = """
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 inTexcoord;
out vec2 outTexcoord;
void main()
{
gl_Position = vec4(position, 1.0f);
outTexcoord = inTexcoord;
}
"""
strFS = """
#version 330 core
out vec4 FragColor;
in vec2 outTexcoord;
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, outTexcoord);
}
"""
class FirstSquare:
def __init__(self):
# load shaders
self.program = glutils.loadShaders(strVS, strFS)
glUseProgram(self.program)
# attributes
self.vertIndex = glGetAttribLocation(self.program, b"position")
self.texIndex = glGetAttribLocation(self.program, b"inTexcoord")
vertices = [
-0.5,-0.5,0,
0.5,-0.5,0,
0.5,0.5,0,
-0.5,0.5,0
]
# texture coords
quadT = [
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0
]
# set up vertex array object (VAO)
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
# set up VBOs
vertexData = numpy.array(vertices, numpy.float32)
self.vertexBuffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER,self.vertexBuffer)
glBufferData(GL_ARRAY_BUFFER, 4 *len(vertexData), vertexData, GL_STATIC_DRAW)
tcData = numpy.array(quadT, numpy.float32)
self.tcBuffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.tcBuffer)
glBufferData(GL_ARRAY_BUFFER, 4*len(tcData), tcData,GL_STATIC_DRAW)
# enable arrays
glEnableVertexAttribArray(self.vertIndex)
glEnableVertexAttribArray(self.texIndex)
# Position attribute
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
glVertexAttribPointer(self.vertIndex, 3, GL_FLOAT, GL_FALSE, 0,None)
# TexCoord attribute
glBindBuffer(GL_ARRAY_BUFFER, self.tcBuffer)
glVertexAttribPointer(self.texIndex, 2, GL_FLOAT, GL_FALSE, 0,None)
# unbind VAO
glBindVertexArray(0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
def render(self,texid ):
self.texid = texid
# enable texture
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.texid)
# use shader
glUseProgram(self.program)
# bind VAO
glBindVertexArray(self.vao)
# draw
glDrawArrays(GL_TRIANGLE_FAN, 0, 4)
# unbind VAO
glBindVertexArray(0)
if __name__ == '__main__':
import sys
import glfw
import OpenGL.GL as gl
def on_key(window, key, scancode, action, mods):
if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
glfw.set_window_should_close(window,1)
# Initialize the library
if not glfw.init():
sys.exit()
# Create a windowed mode window and its OpenGL context
window = glfw.create_window(300, 300, "Texture Square", None, None)
if not window:
glfw.terminate()
sys.exit()
# Make the window's context current
glfw.make_context_current(window)
# Install a key handler
glfw.set_key_callback(window, on_key)
texid = glutils.loadTexture("wall.png")
# Loop until the user closes the window
while not glfw.window_should_close(window):
# Render here
width, height = glfw.get_framebuffer_size(window)
ratio = width / float(height)
gl.glViewport(0, 0, width, height)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
gl.glClearColor(0.2,0.3,0.3,1.0)
firstSquare0 = FirstSquare()
# render
glBindTexture(GL_TEXTURE_2D, texid)
firstSquare0.render(texid)
# Swap front and back buffers
glfw.swap_buffers(window)
# Poll for and process events
glfw.poll_events()
glfw.terminate()
六、参考文献
1、learnopengl教程https://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/06%20Textures/#_1
2、大龙10https://www.jianshu.com/p/4382b25ad797