opengl使用手册,opengl学习教程

首页 > 经验 > 作者:YD1662022-10-25 10:40:20

上图左是平行投影模式的显示效果,上图右是透视投影模式的显示效果。代码如下:

# -*- coding: utf-8 -*-

# -------------------------------------------

# quidam_02.py 旋转、缩放、改变视点和参考点

# -------------------------------------------

from OpenGL.GL import *

from OpenGL.GLU import *

from OpenGL.GLUT import *

import numpy as np

IS_PERSPECTIVE = True # 透视投影

VIEW = np.array([-0.8, 0.8, -0.8, 0.8, 1.0, 20.0]) # 视景体的left/right/bottom/top/near/far六个面

SCALE_K = np.array([1.0, 1.0, 1.0]) # 模型缩放比例

EYE = np.array([0.0, 0.0, 2.0]) # 眼睛的位置(默认z轴的正方向)

LOOK_AT = np.array([0.0, 0.0, 0.0]) # 瞄准方向的参考点(默认在坐标原点)

EYE_UP = np.array([0.0, 1.0, 0.0]) # 定义对观察者而言的上方(默认y轴的正方向)

WIN_W, WIN_H = 640, 480 # 保存窗口宽度和高度的变量

LEFT_IS_DOWNED = False # 鼠标左键被按下

MOUSE_X, MOUSE_Y = 0, 0 # 考察鼠标位移量时保存的起始位置

def getposture:

global EYE, LOOK_AT

dist = np.sqrt(np.power((EYE-LOOK_AT), 2).sum)

if dist > 0:

phi = np.arcsin((EYE[1]-LOOK_AT[1])/dist)

theta = np.arcsin((EYE[0]-LOOK_AT[0])/(dist*np.cos(phi)))

else:

phi = 0.0

theta = 0.0

return dist, phi, theta

DIST, PHI, THETA = getposture # 眼睛与观察目标之间的距离、仰角、方位角

def init:

glClearColor(0.0, 0.0, 0.0, 1.0) # 设置画布背景色。注意:这里必须是4个参数

glEnable(GL_DEPTH_TEST) # 开启深度测试,实现遮挡关系

glDepthFunc(GL_LEQUAL) # 设置深度测试函数(GL_LEQUAL只是选项之一)

def draw:

global IS_PERSPECTIVE, VIEW

global EYE, LOOK_AT, EYE_UP

global SCALE_K

global WIN_W, WIN_H

# 清除屏幕及深度缓存

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

# 设置投影(透视投影)

glMatrixMode(GL_PROJECTION)

glLoadIdentity

if WIN_W > WIN_H:

if IS_PERSPECTIVE:

glFrustum(VIEW[0]*WIN_W/WIN_H, VIEW[1]*WIN_W/WIN_H, VIEW[2], VIEW[3], VIEW[4], VIEW[5])

else:

glOrtho(VIEW[0]*WIN_W/WIN_H, VIEW[1]*WIN_W/WIN_H, VIEW[2], VIEW[3], VIEW[4], VIEW[5])

else:

if IS_PERSPECTIVE:

glFrustum(VIEW[0], VIEW[1], VIEW[2]*WIN_H/WIN_W, VIEW[3]*WIN_H/WIN_W, VIEW[4], VIEW[5])

else:

glOrtho(VIEW[0], VIEW[1], VIEW[2]*WIN_H/WIN_W, VIEW[3]*WIN_H/WIN_W, VIEW[4], VIEW[5])

# 设置模型视图

glMatrixMode(GL_MODELVIEW)

glLoadIdentity

# 几何变换

glScale(SCALE_K[0], SCALE_K[1], SCALE_K[2])

# 设置视点

gluLookAt(

EYE[0], EYE[1], EYE[2],

LOOK_AT[0], LOOK_AT[1], LOOK_AT[2],

EYE_UP[0], EYE_UP[1], EYE_UP[2]

)

# 设置视口

glViewport(0, 0, WIN_W, WIN_H)

# ---------------------------------------------------------------

glBegin(GL_LINES) # 开始绘制线段(世界坐标系)

# 以红色绘制x轴

glColor4f(1.0, 0.0, 0.0, 1.0) # 设置当前颜色为红色不透明

glVertex3f(-0.8, 0.0, 0.0) # 设置x轴顶点(x轴负方向)

glVertex3f(0.8, 0.0, 0.0) # 设置x轴顶点(x轴正方向)

# 以绿色绘制y轴

glColor4f(0.0, 1.0, 0.0, 1.0) # 设置当前颜色为绿色不透明

glVertex3f(0.0, -0.8, 0.0) # 设置y轴顶点(y轴负方向)

glVertex3f(0.0, 0.8, 0.0) # 设置y轴顶点(y轴正方向)

# 以蓝色绘制z轴

glColor4f(0.0, 0.0, 1.0, 1.0) # 设置当前颜色为蓝色不透明

glVertex3f(0.0, 0.0, -0.8) # 设置z轴顶点(z轴负方向)

glVertex3f(0.0, 0.0, 0.8) # 设置z轴顶点(z轴正方向)

glEnd # 结束绘制线段

# ---------------------------------------------------------------

glBegin(GL_TRIANGLES) # 开始绘制三角形(z轴负半区)

glColor4f(1.0, 0.0, 0.0, 1.0) # 设置当前颜色为红色不透明

glVertex3f(-0.5, -0.366, -0.5) # 设置三角形顶点

glColor4f(0.0, 1.0, 0.0, 1.0) # 设置当前颜色为绿色不透明

glVertex3f(0.5, -0.366, -0.5) # 设置三角形顶点

glColor4f(0.0, 0.0, 1.0, 1.0) # 设置当前颜色为蓝色不透明

glVertex3f(0.0, 0.5, -0.5) # 设置三角形顶点

glEnd # 结束绘制三角形

# ---------------------------------------------------------------

glBegin(GL_TRIANGLES) # 开始绘制三角形(z轴正半区)

glColor4f(1.0, 0.0, 0.0, 1.0) # 设置当前颜色为红色不透明

glVertex3f(-0.5, 0.5, 0.5) # 设置三角形顶点

glColor4f(0.0, 1.0, 0.0, 1.0) # 设置当前颜色为绿色不透明

glVertex3f(0.5, 0.5, 0.5) # 设置三角形顶点

glColor4f(0.0, 0.0, 1.0, 1.0) # 设置当前颜色为蓝色不透明

glVertex3f(0.0, -0.366, 0.5) # 设置三角形顶点

glEnd # 结束绘制三角形

# ---------------------------------------------------------------

glutSwapBuffers # 切换缓冲区,以显示绘制内容

def reshape(width, height):

global WIN_W, WIN_H

WIN_W, WIN_H = width, height

glutPostRedisplay

def mouseclick(button, state, x, y):

global SCALE_K

global LEFT_IS_DOWNED

global MOUSE_X, MOUSE_Y

MOUSE_X, MOUSE_Y = x, y

if button == GLUT_LEFT_BUTTON:

LEFT_IS_DOWNED = state==GLUT_DOWN

elif button == 3:

SCALE_K *= 1.05

glutPostRedisplay

elif button == 4:

SCALE_K *= 0.95

glutPostRedisplay

def mousemotion(x, y):

global LEFT_IS_DOWNED

global EYE, EYE_UP

global MOUSE_X, MOUSE_Y

global DIST, PHI, THETA

global WIN_W, WIN_H

if LEFT_IS_DOWNED:

dx = MOUSE_X - x

dy = y - MOUSE_Y

MOUSE_X, MOUSE_Y = x, y

PHI = 2*np.pi*dy/WIN_H

PHI %= 2*np.pi

THETA = 2*np.pi*dx/WIN_W

THETA %= 2*np.pi

r = DIST*np.cos(PHI)

EYE[1] = DIST*np.sin(PHI)

EYE[0] = r*np.sin(THETA)

EYE[2] = r*np.cos(THETA)

if 0.5*np.pi < PHI < 1.5*np.pi:

EYE_UP[1] = -1.0

else:

EYE_UP[1] = 1.0

glutPostRedisplay

def keydown(key, x, y):

global DIST, PHI, THETA

global EYE, LOOK_AT, EYE_UP

global IS_PERSPECTIVE, VIEW

if key in [b'x', b'X', b'y', b'Y', b'z', b'Z']:

if key == b'x': # 瞄准参考点 x 减小

LOOK_AT[0] -= 0.01

elif key == b'X': # 瞄准参考 x 增大

LOOK_AT[0] = 0.01

elif key == b'y': # 瞄准参考点 y 减小

LOOK_AT[1] -= 0.01

elif key == b'Y': # 瞄准参考点 y 增大

LOOK_AT[1] = 0.01

elif key == b'z': # 瞄准参考点 z 减小

LOOK_AT[2] -= 0.01

elif key == b'Z': # 瞄准参考点 z 增大

LOOK_AT[2] = 0.01

DIST, PHI, THETA = getposture

glutPostRedisplay

elif key == b'\r': # 回车键,视点前进

EYE = LOOK_AT (EYE - LOOK_AT) * 0.9

DIST, PHI, THETA = getposture

glutPostRedisplay

elif key == b'\x08': # 退格键,视点后退

EYE = LOOK_AT (EYE - LOOK_AT) * 1.1

DIST, PHI, THETA = getposture

glutPostRedisplay

elif key == b' ': # 空格键,切换投影模式

IS_PERSPECTIVE = not IS_PERSPECTIVE

glutPostRedisplay

if __name__ == "__main__":

glutInit

displayMode = GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH

glutInitDisplayMode(displayMode)

glutInitWindowSize(WIN_W, WIN_H)

glutInitWindowPosition(300, 200)

glutCreateWindow('Quidam Of OpenGL')

init # 初始化画布

glutDisplayFunc(draw) # 注册回调函数draw

glutReshapeFunc(reshape) # 注册响应窗口改变的函数reshape

glutMouseFunc(mouseclick) # 注册响应鼠标点击的函数mouseclick

glutMotionFunc(mousemotion) # 注册响应鼠标拖拽的函数mousemotion

glutKeyboardFunc(keydown) # 注册键盘输入的函数keydown

glutMainLoop # 进入glut主循环

十二、小结

虽然还有很多领域需要我们继续探索,比如灯光、材质、雾化、拾取等,但那不是奇幻之旅的目标。奇幻之旅仅仅是帮助读者建立 OpenGL 的基本概念。至此,我们基本完成了任务。

加速渲染

实际应用 OpenGL 绘制三维图像时,往往需要处理数以万计的顶点,有时甚至是百万级、千万级。我们通常不会在绘制函数里面传送这些数据,而是在绘制之前,将这些数据提前传送到GPU。绘制函数每次绘制时,只需要从GPU的缓存中取出数据即可,极大地提高了效率。这个机制地实现,依赖于顶点缓冲区对象(Vertex Buffer Object),简称VBO。

尽管 VBO 是显卡的扩展,其实没有用到GPU运算,也就是说 VBO 不用写着色语言,直接用opengl函数就可以调用,主要目的是用于加快渲染的速。

VBO 将顶点信息放到 GPU 中,GPU 在渲染时去缓存中取数据,二者中间的桥梁是 GL-Context。GL-Context 整个程序一般只有一个,所以如果一个渲染流程里有两份不同的绘制代码,GL-context 就负责在他们之间进行切换。这也是为什么要在渲染过程中,在每份绘制代码之中会有 glBindbuffer、glEnableVertexAttribArray、glVertexAttribPointer。如果把这些都放到初始化时候完成,使用一种结构记录该次绘制所需要的所有 VBO 所需信息,把它保存到 VBO特定位置,绘制的时候直接在这个位置取信息绘制,会简化渲染流程、提升渲染速度。这就是 VAO 概念产生的初衷。

VAO 的全名是 Vertex Array Object,首先,它不是 Buffer-Object,所以不用作存储数据;其次,它针对“顶点”而言,也就是说它跟“顶点的绘制”息息相关。VAO 记录的是一次绘制中所需要的信息,这包括“数据在哪里 glBindBuffer”、“数据的格式是怎么样的 glVertexAttribPointer”、shader-attribute 的 location 的启用 glEnableVertexAttribArray。

根据我查到的资料,几乎所有的显卡都支持 VBO,但不是所有的显卡都支持 VAO,而 VAO 仅仅是优化了 VBO 的使用方法,对于加速并没有实质性的影响,因此本文只讨论 VBO 技术。

一、创建顶点缓冲区对象(VBO)

假定画一个六面体,顶点是这样的:

# -*- coding: utf-8 -*-

# 六面体数据

# ------------------------------------------------------

# v4----- v5

# /| /|

# v0------v1|

# | | | |

# | v7----|-v6

# |/ |/

# v3------v2

# 顶点集

vertices = np.array([

-0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, # v0-v1-v2-v3

-0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5 # v4-v5-v6-v7

], dtype=np.float32)

# 索引集

indices = np.array([

0, 1, 2, 3, # v0-v1-v2-v3 (front)

4, 5, 1, 0, # v4-v5-v1-v0 (top)

3, 2, 6, 7, # v3-v2-v6-v7 (bottom)

5, 4, 7, 6, # v5-v4-v7-v6 (back)

1, 5, 6, 2, # v1-v5-v6-v2 (right)

4, 0, 3, 7 # v4-v0-v3-v7 (left)

], dtype=np.int)

在GPU上创建VBO如下:

from OpenGL.arrays import vbo

vbo_vertices = vbo.VBO(vertices)

vbo_indices = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)

创建 顶点 VBO 时,默认 target=GL_ARRAY_BUFFER, 而创建索引 VBO 时,target=GL_ELEMENT_ARRAY_BUFFER,因为顶点的数据类型是 np.float32,索引的数据类型是np.int。

在VBO保存的顶点数据集,除了顶点信息外,还可以包含颜色、法线、纹理等数据,这就是顶点混合数组的概念。假定我们在上面的顶点集中增加每个顶点的颜色,则可以写成这样:

vertices = np.array([

0.3, 0.6, 0.9, -0.35, 0.35, 0.35, # c0-v0

0.6, 0.9, 0.3, 0.35, 0.35, 0.35, # c1-v1

0.9, 0.3, 0.6, 0.35, -0.35, 0.35, # c2-v2

0.3, 0.9, 0.6, -0.35, -0.35, 0.35, # c3-v3

0.6, 0.3, 0.9, -0.35, 0.35, -0.35, # c4-v4

0.9, 0.6, 0.3, 0.35, 0.35, -0.35, # c5-v5

0.3, 0.9, 0.9, 0.35, -0.35, -0.35, # c6-v6

0.9, 0.9, 0.3, -0.35, -0.35, -0.35 # c7-v7

], dtype=np.float32)

二、分离顶点混合数组

使用 glInterleavedArrays 函数可以从顶点混合数组中分离顶点、颜色、法线和纹理。比如,对只包含顶点信息的顶点混合数组:

vbo_indices.bind

glInterleavedArrays(GL_V3F, 0, None)

如果顶点混合数组包含了颜色和顶点信息:

vbo_indices.bind

glInterleavedArrays(GL_C3F_V3F, 0, None)

glInterleavedArrays 函数第一个参数总共有14个选项,分别是:

三、使用顶点缓冲区对象(VBO)

使用glDrawElements 等函数绘制前,需要先绑定顶点数据集和索引数据集,然后使用glInterleavedArrays 分理出顶点、颜色、法线等数据。

vbo_indices.bind

glInterleavedArrays(GL_V3F, 0, None)

vbo_indices.bind

glDrawElements(GL_QUADS, int(vbo_indices .size/4), GL_UNSIGNED_INT, None)

vbo_indices.unbind

vbo_indices.unbind

致谢:

写作过程中,我参考了很多资料,包括纸质书籍和网页,列写于此,一并致谢!

原文:https://blog.csdn.net/xufive/article/details/86565130

声明:本文系CSDN博客原创文章,转载请联系原作者。

技术的道路一个人走着极为艰难?

一身的本领得不施展?

优质的文章得不到曝光?

上一页123末页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.