如何在Pygame+OpenGL场景中固定光源位置,实现旋转球体昼夜效果
问题解决方法
核心问题根源
你的代码存在两个核心错误导致光源随球体同步旋转:
- 矩阵栈用途错误:你将球体的平移、旋转、缩放等模型变换放到了
GL_PROJECTION投影矩阵栈中,投影矩阵仅应该负责视口投影逻辑(比如glOrtho),所有物体的空间变换都应该放在GL_MODELVIEW模型视图矩阵栈中,矩阵栈用途混乱直接导致光照计算异常。 - 光源设置时机错误:你在应用球体旋转变换之前就设置了光源位置,OpenGL的
glLightfv(GL_LIGHT0, GL_POSITION)会记录调用时的当前模型视图矩阵,将光源位置绑定到对应的坐标系下,你的设置时机导致光源被绑定到了球体的局部坐标系,自然会跟着球体一起旋转。
具体修改方案
直接替换你的esfep函数为以下内容即可:
def esfep(vesf,resol,texture,rotpt,punt,tama): light_ambient = [0.0, 0.0, 0.0, 1.0] light_diffuse = [1.0, 1.0, 1.0, 1.0] # 方向光配置,w=0代表是平行光(模拟太阳光),前三个值为光照方向 light_position = [1, 0, 0, 0.0] # 第一步:设置投影矩阵,仅做投影变换 glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0,resol[0],resol[1],0,-resol[0],resol[1]) # 第二步:切换到模型视图矩阵,初始加载单位矩阵(对应世界坐标系原点) glMatrixMode(GL_MODELVIEW) glLoadIdentity() # 第三步:在世界坐标系下设置固定光源,此时设置的光源位置不会随后续物体变换移动 glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient) glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse) glLightfv(GL_LIGHT0, GL_POSITION, light_position) # 第四步:仅在模型视图矩阵上应用球体的平移、旋转、缩放变换 rotpt = rotpt + vesf glTranslatef(float(punt[0]),float(punt[1]),-resol[1]) glRotatef(270, 1, 0, 0) glRotatef(rotpt, 0, 0, 1) glScalef(1*tama/resol[1],1*tama/resol[1],1*tama/resol[1]) # 原有绘制逻辑保持不变 glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) glEnable(GL_COLOR_MATERIAL) glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) esf = gluNewQuadric() gluQuadricTexture(esf, GL_TRUE) glDisable(GL_DEPTH_TEST) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, texture) gluSphere(esf,round(resol[1]/2), 50, 50) gluDeleteQuadric(esf) glDisable(GL_TEXTURE_2D) glDisable(GL_DEPTH_TEST) return rotpt
额外注意事项
你的原代码中sup_texture函数存在变量名笔误:定义的纹理ID变量为textID,但glBindTexture时使用了texID,需要统一变量名否则会运行报错。
如果需要模拟点光源效果,只需要将light_position的第四个参数改为1,前三个参数改为你期望的光源世界坐标即可,例如[1000, 0, 0, 1.0]就代表光源固定在世界坐标X轴1000的位置。
内容的提问来源于stack exchange,提问作者Calvarez
相关产品推荐
相关产品推荐

