一、目的
1、实现环境光照射下的棋盘球体;
二、程序运行结果
三、光照的基本模型
如果要用一个数学模型完全真实地描述现实世界中的光照是很难的,一方面数学模型本身可能太过复杂,另一方面复杂的模型可能导致巨大的计算量。因此,OpenGL中采用的光照模型相对现实世界进行了很大的简化,将光照分成了3种组成元素(也可以称为3个通道),包括:
- 环境光、
- 散射光
-
镜面光
具体情况如图6-5所示。
四、环境光
环境光(Ambient)指的是从四面八方照射到物体上,全方位360°都均匀的光。其代表的是现实世界中从光源射出,经过多次反射后,各方向基本均匀的光。环境光最大的特点是不依赖于光源的位置,而且没有方向性,图6-6简单地说明了这个问题。
从图6-6中可以看出,环境光不但入射是均匀的,反射也是各向均匀的。用于计算环境光的数学模型非常简单,具体公式如下。
环境光照射结果=材质的反射系数×环境光强度
程序说明:片元着色器代码与前面案例的基本相同,主要是增加了接收环境光强度,以及使用环境光强度与片元本身颜色值加权计算产生最终片元颜色值的相关代码。
五、源代码
"""
程序名称:GL_DrawBall03.py
编程: dalong10
功能: 环境光的应用实现
参考资料: 《OpenGL ES 3.x游戏开发》(上卷)吴亚峰
"""
import myGL_Funcs #Common OpenGL utilities,see myGL_Funcs.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 pyrr import Quaternion, matrix44, Vector3
strVS = """
#version 330 core
layout(location = 0) in vec3 position;
uniform mat4 uMVMatrix;
out vec3 vPosition ; //用于传递给片元着色器的顶点位置
out vec4 vAmbient; //用于传递给片元着色器的环境光分量
void main(){
gl_Position= uMVMatrix* vec4(position.x, position.y, position.z, 1.0);
//将顶点的位置传给片元着色器
vPosition = position;//将原始顶点位置传递给片元着色器
//将环境光强度传给片元着色器
vAmbient = vec4(0.15,0.15,0.15,1.0);
}
"""
strFS = """
#version 330 core
in vec3 vPosition;//接收从顶点着色器过来的顶点位置
in vec4 vAmbient;//接收从顶点着色器过来的环境光强度
out vec4 fragColor;//输出的片元颜色
void main(){
vec3 color;
float n = 8.0;//外接立方体每个坐标轴方向切分的份数
float uR=0.8 ;
float span = 2.0*uR/n;//每一份的尺寸(小方块的边长)
int i = int((vPosition.x + uR)/span);//当前片元位置小方块的行数
int j = int((vPosition.y + uR)/span);//当前片元位置小方块的层数
int k = int((vPosition.z + uR)/span);//当前片元位置小方块的列数
//计算当前片元行数、层数、列数的和并对2取模
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor == 1) {//奇数时为红色
color = vec3(0.678,0.231,0.129);//红色
}
else {//偶数时为白色
color = vec3(1.0,1.0,1.0);//白色
}
//根据环境光强度计算最终片元颜色值
fragColor=vec4(color,0)*vAmbient;;
}
"""
cameraPos=np.array([0.0, 0.0, 30]) # 眼睛的位置(默认z轴的正方向)
cameraFront=np.array([0.0, 0.0, 0.0]) # 瞄准方向的参考点(默认在坐标原点)
cameraUp=np.array([0.0, 1.0, 0.0]) # 定义对观察者而言的上方(默认y轴的正方向)
WIN_W, WIN_H = 640, 480 # 保存窗口宽度和高度的变量
class FirstSphere:
def __init__(self, cube_verticeside ):
# load shaders
self.program = myGL_Funcs.loadShaders(strVS, strFS)
glUseProgram(self.program)
self.vertIndex = glGetAttribLocation(self.program, b"position")
self.cube_vertices = cube_verticeside
# set up vertex array object (VAO)
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
# set up VBOs
vertexData = numpy.array(self.cube_vertices, numpy.float32)
self.vertexBuffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
glBufferData(GL_ARRAY_BUFFER, 4*len(vertexData), vertexData, GL_STATIC_DRAW)
# enable arrays
glEnableVertexAttribArray(self.vertIndex)
# Position attribute
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
glVertexAttribPointer(self.vertIndex, 3, GL_FLOAT, GL_FALSE, 0,None)
# unbind VAO
glBindVertexArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
def render(self, mvMatrix):
# use shader
glUseProgram(self.program)
# set modelview matrix
glUniformMatrix4fv(glGetUniformLocation(self.program, 'uMVMatrix'),
1, GL_FALSE, mvMatrix)
# bind VAO
glBindVertexArray(self.vao)
# draw
glDrawArrays(GL_TRIANGLES,0,len(self.cube_vertices) )
# unbind VAO
glBindVertexArray(0)
def drawglobeVBO():
PI = 3.14159265358979323846264
statcky = 30 # 横向向切成多少片
stlicex = 30 # 纵向切多少片
R = 0.8 # 半径
angleHy = (2*PI)/statcky # 横向每份的角度 算出弧度值
angleZx = (2*PI)/stlicex; # 纵向每份的角度 算出弧度值
NumAngleHy = 0.0 # 当前横向角度
NumAngleZx = 0.0 # 当前纵向角度
c=numpy.array([], numpy.float32)
for j in range(statcky):
for i in range(stlicex):
NumAngleHy = angleHy*i #
NumAngleZx = angleZx*j # 起点都是轴指向的方向。根据右手定则决定转向,只要转向相同,那么两个就合适
x0 = R*np.cos(NumAngleHy)*np.cos(NumAngleZx)
y0 = R*np.cos(NumAngleHy)*np.sin(NumAngleZx)
z0 = R*np.sin(NumAngleHy)
x1 = R*np.cos(NumAngleHy)*np.cos(NumAngleZx+angleZx)
y1 = R*np.cos(NumAngleHy)*np.sin(NumAngleZx+angleZx)
z1 = R*np.sin(NumAngleHy)
x2 = R*np.cos(NumAngleHy+angleHy)*np.cos(NumAngleZx+angleZx)
y2 = R*np.cos(NumAngleHy+angleHy)*np.sin(NumAngleZx+angleZx)
z2 = R*np.sin(NumAngleHy+angleHy)
x3 = R*np.cos(NumAngleHy+angleHy)*np.cos(NumAngleZx)
y3 = R*np.cos(NumAngleHy+angleHy)*np.sin(NumAngleZx)
z3 = R*np.sin(NumAngleHy+angleHy)
c=np.hstack((c,numpy.array([x1,y1,z1], numpy.float32) ))
c=np.hstack((c,numpy.array([x3,y3,z3], numpy.float32) ))
c=np.hstack((c,numpy.array([x0,y0,z0], numpy.float32) ))
c=np.hstack((c,numpy.array([x1,y1,z1], numpy.float32) ))
c=np.hstack((c,numpy.array([x2,y2,z2], numpy.float32) ))
c=np.hstack((c,numpy.array([x3,y3,z3], numpy.float32) ))
return c
#Is called whenever a key is pressed/released via GLFW
def on_key(window, key, scancode, action, mods):
if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
glfw.set_window_should_close(window,1)
if __name__ == '__main__':
import sys
import glfw
import OpenGL.GL as gl
keys=numpy.zeros(1024)
deltaTime = 0.0
lastFrame = 0.0 # Time of last frame
# Initialize the library
if not glfw.init():
sys.exit()
# Create a windowed mode window and its OpenGL context
window = glfw.create_window(640, 480, "GL_DrawBall03 ", 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)
PI = 3.14159265358979323846264
# 画球面
vert = drawglobeVBO()
# Loop until the user closes the window
a=0
firstSphere1 = FirstSphere(vert)
while not glfw.window_should_close(window):
currentFrame = glfw.get_time()
deltaTime = currentFrame - lastFrame
lastFrame = currentFrame
# Render here
width, height = glfw.get_framebuffer_size(window)
WIN_W, WIN_H =width, height
ratio = width / float(height)
glfw.poll_events()
gl.glViewport(0, 0, width, height)
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
#glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); #用于控制多边形的显示方式
gl.glMatrixMode(gl.GL_PROJECTION)
gl.glLoadIdentity()
gl.glOrtho(-ratio, ratio, -1, 1, 1, -1)
gl.glMatrixMode(gl.GL_MODELVIEW)
gl.glLoadIdentity()
gl.glClearColor(0.0,0.1,0.1,1.0)
# modelview matrix
mvMatrix = matrix44.create_look_at(cameraPos, cameraFront, cameraUp,None) # 设置视点
pMatrix = matrix44.create_perspective_projection_from_bounds(-ratio*1.0, ratio*1.0, -1, 1,20,100,None)
model0 = matrix44.multiply(mvMatrix,pMatrix)
trans1 = matrix44.create_from_translation(Vector3([-0.6, 0, 0]))
trans2 = matrix44.create_from_translation(Vector3([0.6, 0, 0]))
model1 = matrix44.multiply(model0,trans1)
model2 = matrix44.multiply(model0,trans2)
firstSphere1.render( model1) #球1
firstSphere1.render( model2) #球2
# Swap front and back buffers
glfw.swap_buffers(window)
# Poll for and process events
glfw.poll_events()
glfw.terminate()
六、参考资料
1、大龙10的简书:https://www.jianshu.com/p/49dec482a291
2、吴亚峰《OpenGL ES 3.x游戏开发》(上卷)