姓名:卜凡
学号:2020211066
指导老师:符强
日期:2022-03-15 ~ 17
非渲染部分,也就是除了三角形绘制之外大体的代码框架,如下。
from OpenGL.GL import * # 导入 OpenGL 库
from OpenGL.GLUT import * # 导入 GLUT (OpenGL Utility Toolkit) 库
import time # 导入时间与日期库
### 全局变量
global t_begin, t # 初始时间戳, 当前时间
t_begin = t = 0
### 重绘方法
def Draw():
# 重置画面,重置图像缓存和深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
### >> LATER: 三角形绘制(见下)
# 强制刷新缓冲,保证绘图命令将被执行
glFlush()
### 空闲事件方法
def OnIdle():
global t_begin, t
t = time.time() - t_begin # 更新当前时间
glutPostRedisplay() # 标记当前窗口需要重新绘制
### 主方法
if __name__ == "__main__":
### 1. 初始化
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(720, 720) # 窗口大小
glutCreateWindow("Homework 1 By July") # 窗口标题,创建窗口
# >> TODO: 修改这里开启(glEnable())和关闭(glDisable())深度校验
glEnable(GL_DEPTH_TEST) # 开启深度校验
glClearColor(1.0, 1.0, 1.0, 1.0) # 设置 glClear 的背景色为 白色
glOrtho(-1.2, 1.2, -1.2, 1.2, 2, -2) # 设置正交投影的范围
### 2. 设置函数
glutDisplayFunc(Draw) # 重绘方法
# glutReshapeFunc(OnResize) # 窗口大小更新事件方法
# glutKeyboardFunc(OnKey) # 键盘事件方法
glutIdleFunc(OnIdle) # 空闲时间方法
### 3. 开始主循环
t_begin = time.time() # 记录初始时间戳
glutMainLoop()
这里引入了 Python 自带的 time
日期与时间模块,并创建两个全局变量 t_begin
和 t
来分别表示初始时间戳和当前程序执行时间(均为浮点数,单位均为秒),在开始主循环的时候初始化 t_begin
,并在空闲时间方法 OnIdle()
中更新 t
,以便后续能够根据 t
实现物体的运动。
渲染一个在 $z=0$ 平面上固定不动的三角形 $\triangle PQR$ ,其三个顶点的坐标分别位于$P(-1, -1, 0), \ Q(-0.5, 1, 0), \ R(1, -1, 0)$。 它将整体被渲染为红色( RGBA(1, 0, 0, 1)
)。
另,渲染一个随全局时间 $t$(即 t
)沿 $z$ 轴做正弦往复运动的运动三角形 $\triangle ABC$ 。
Fig 2-1 所绘制图形的立体图示
颜色:此三角形的各顶点将随 $z$ 坐标的远近而取不同颜色,越近越绿,越远越黑。具体说来,对每个顶点而言,其颜色将随该点的 $z$ 坐标在 $[-2, 1]$ 中的取值而取 $[$绿色,黑色$]$ ( [RGBA(0,1,0,1), RGBA(0,0,0,1)]
)中线性对应的值,三角形内部则作默认插值。
为实现上述描述,编写区间线性映射算法如下:给定要映射的原值 $v_0$、原区间 $[l_0, r_0]$、新区间 $[l, r]$,构造区间线性映射 $[l_0, r_0] \to [l, r]$ 并获取原值在新区间尺度下的取值 $v$ 而返回。
依据线性映射,有等比例公式:
$$ \frac{v-l}{r-l} = \frac{v_0-l_0}{r_0-l_0} $$
可得到结果如下(规定当给入原区间为空区间时,给出结果为新区间的左值)。
$$ v= \begin{cases} \frac{(v_0-l_0)(r-l)}{r_0-l_0} + l ,& r_0 \neq l_0 \\ l,& r_0 = l_0 \end{cases} $$
代码如下。它支持进行结果的区间限制 (Clamp),也支持反向区间(即左值大右值小的区间)。
# 区间映射方法
def IntervalMapF(v0:float, l0:float, r0:float, l:float, r:float, clamp:bool):
if clamp: # 进行结果的区间限制
if (r0 >= l0): # 正向区间或空区间
if (v0 <= l0): # 左值外部(更小)
return l
elif (v0 >= r0): # 右值外部(更大)
return r
else: # 反向区间
if (v0 >= l0): # 左值外部(更大)
return l # 返回新左值
elif (v0 <= r0): # 右值外部(更小)
return r # 返回新右值
if (r0 - l0 == 0): # 空区间
return l
else: # 非空区间
return (v0 - l0) * (r - l) / (r0 - l0) + l