如何在PyQtGraph的GLMeshItem中设置光源位置与阴影效果?
解决pyqtgraph.opengl GLMeshItem光源与相机视角同步的问题
pyqtgraph的GLMeshItem默认Phong着色并未暴露直接调整光源位置的API,但可以通过自定义着色器+实时绑定相机位置实现光源随相机视角移动,解决模型过暗、光源位置异常的问题。
核心思路
- 替换默认着色器,新增
lightPosuniform变量接收相机位置 - 在渲染循环中实时获取相机世界坐标,传递给着色器
- 修改着色器中的光源方向计算逻辑,让光源始终跟随相机
完整实现代码
import pyqtgraph.opengl as gl from pyqtgraph.Qt import QtCore, QtGui import numpy as np from stl import mesh # 加载STL模型 stl_mesh = mesh.Mesh.from_file("your_model.stl") vertices = stl_mesh.vectors.reshape(-1, 3) faces = np.arange(len(vertices)).reshape(-1, 3) app = QtGui.QApplication([]) w = gl.GLViewWidget() w.show() w.setWindowTitle('STL Viewer with Camera-Linked Light') # 创建GLMeshItem mesh_item = gl.GLMeshItem(vertexes=vertices, faces=faces, color=(1,1,1,1), smooth=True, shaded=True) w.addItem(mesh_item) # 自定义顶点着色器 vertex_shader = """ attribute vec3 vertex; attribute vec3 normal; attribute vec4 color; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform mat3 normalMatrix; uniform vec3 lightPos; varying vec3 vNormal; varying vec4 vColor; varying vec3 vLightDir; void main() { vNormal = normalize(normalMatrix * normal); vColor = color; vec4 viewPos = modelViewMatrix * vec4(vertex, 1.0); vLightDir = normalize(lightPos - viewPos.xyz); gl_Position = projectionMatrix * modelViewMatrix * vec4(vertex, 1.0); } """ # 自定义片段着色器 fragment_shader = """ varying vec3 vNormal; varying vec4 vColor; varying vec3 vLightDir; void main() { // 调整ambient值可优化模型整体亮度 float ambient = 0.3; float diff = max(dot(vNormal, vLightDir), 0.0); // 调整高光指数(16.0)可改变高光区域大小 float spec = pow(max(dot(reflect(-vLightDir, vNormal), vec3(0,0,1)), 0.0), 16.0); vec3 color = vColor.rgb * (ambient + diff) + vec3(1.0) * spec; gl_FragColor = vec4(color, vColor.a); } """ # 替换MeshItem的着色器 shader_program = gl.GLShaderProgram(vertex_shader, fragment_shader) mesh_item.setShader(shader_program) # 实时更新光源位置(绑定相机) def update_light_source(): # 获取相机的世界空间位置 camera_pos = w.cameraPosition() shader_program.setUniformValue('lightPos', camera_pos) # 每帧更新(约60fps) update_timer = QtCore.QTimer() update_timer.timeout.connect(update_light_source) update_timer.start(16) QtGui.QApplication.instance().exec_()
关键说明
w.cameraPosition()返回相机在世界空间的坐标,确保光源始终跟随相机视角- 着色器中通过
lightPos - viewPos.xyz计算光源方向,实现相机位置作为光源的效果 - 调整
ambient(环境光强度)、高光指数(代码中16.0)可解决模型过暗问题,根据需求微调 - 若之前自定义着色器无效,大概率是未实时更新uniform变量或光源方向计算错误(混淆了视图空间与世界空间坐标)
内容的提问来源于stack exchange,提问作者Nagarjun Vinukonda
相关产品推荐
相关产品推荐

