PyQT5 OpenGL zoom - python

So I wanted to make a zoom functionality in PyQT. And from answer 8.040 I implemented it as such:
class GLWindow(QOpenGLWidget):
def __init__(self, parent=None):
super(GLWindow, self).__init__(parent)
self.object = 0
self.zoomFactor = 1.0
self.setFocusPolicy(Qt.WheelFocus)
self.lastPos = QPoint()
and
def resizeGL(self, width, height):
gl.glViewport(0, 0, width, height)
gl.glMatrixMode(gl.GL_PROJECTION)
gl.glLoadIdentity()
gl.glOrtho(-2 * self.zoomFactor, +2 * self.zoomFactor, +2 * self.zoomFactor, -2 * self.zoomFactor, 4.0, 15.0)
gl.glMatrixMode(gl.GL_MODELVIEW)
Now at the start of the program if I change the self.zoomFactor to let's say 2.0, it has the desired effect of a zoom, but I can't seem to resize it while running.
def wheelEvent(self, event):
"""http://doc.qt.io/qt-5/qwheelevent.html"""
scroll = event.angleDelta()
if scroll.y() > 0: # up
self.zoomFactor += 0.1
self.resizeGL(self.width(), self.height())
self.update()
pass
else: # down
pass
The above code is how I tried it already, without the desired effect. It catches the upwards zoom and calls the resizeGL function, but won't zoom in/out.

Got the answer. It does work. The resizeGL only updates when resizing the window, so we have to use it in another method.

Maybe it will help someone else:
def paintGL(self, coordinates=None):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glPushMatrix()
glTranslate(0.0, 0.0, -50.0)
glScale(0.1 * self.zoomFactor, 0.1 * self.zoomFactor, 0.1 * self.zoomFactor)
glRotate(self.rotX, 1.0, 0.0, 0.0)
glRotate(self.rotY, 0.0, 1.0, 0.0)
glRotate(self.rotZ, 0.0, 0.0, 1.0)
glTranslate(-0.5, -0.5, -0.5)
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT, 0, self.vertVBO)
glDrawElements(GL_QUADS, len(self.edges), GL_UNSIGNED_INT, self.edges)
glDisableClientState(GL_VERTEX_ARRAY)
glPopMatrix()
def wheelEvent(self, event):
scroll = event.angleDelta()
if scroll.y() > 0:
self.zoomFactor += 0.1
self.update()
else:
self.zoomFactor -= 0.1
self.update()
def initializeGL(self):
glClearColor(0.0, 0.0, 0.0, 0.0)
glEnable(GL_DEPTH_TEST)
self.initGeometry()
self.rotX = 0.0
self.rotY = 0.0
self.rotZ = 0.0
self.zoomFactor = 1.0
To zoom in/zoom out you must call glScale() function before render shape instead resizeGL()

Related

Consider a circle (𝑥 − 40) 2 + (𝑦 − 40) 2 = 400. Rotate it along X- axis counter clockwise 30 degree & translate it along Z- axis for +20 units

Write a PYOpenGL code for this operation.
I am able to draw the circle but my code for rotating and translating is not working. The code is executing but not giving the correct result. Help me with the rotation and translation part of the question.
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import *
def circle():
posx, posy = 40,40
sides = 80
radius = 20
glBegin(GL_POLYGON)
for i in range(100):
cosine= radius * cos(i*2*pi/sides) + posx
sine = radius * sin(i*2*pi/sides) + posy
glVertex2f(cosine,sine)
glEnd()
def iterate():
glViewport(0, 0, 3000, 3000)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
# glMatrixMode(GL_MODELVIEW)
# glLoadIdentity()
# glTranslatef(0, 0, -3)
# glRotatef(50, 1, 0, 0)
# glRotatef(70, 0, 1, 0)
glOrtho(0.0, 500, 0.0, 500, 0.0, 1.0)
glMatrixMode (GL_MODELVIEW)
glLoadIdentity()
def showScreen():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
iterate()
glColor3f(1.0, 0.0, 3.0)
circle()
glutSwapBuffers()
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(400, 400)
glutInitWindowPosition(200, 200)
wind = glutCreateWindow("OpenGL Coding Practice")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutMainLoop()
When you transalte the circle along the z axis or rotate the circle it is clipped by the near (= 0) and far plane (= 1) of the Orthographic projection.
Change the distance to the near and far plane (e.g. -100 and 100):
glOrtho(0.0, 500, 0.0, 500, 0.0, 1.0)
glOrtho(0.0, 500, 0.0, 500, -100, 100)
iterate function:
def iterate():
glViewport(0, 0, 3000, 3000)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0.0, 500, 0.0, 500, -100, 100)
glMatrixMode (GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, 20)
glRotatef(30, 1, 0, 0)

python opengl with glfw mouse wheel zoom too further.the camera cover data object

i have two questions.
I's normal in most cases when i use mouse wheel to zoom in.Once I zoom in to a certain level,it will zoom in farther.(near the center)
When the object(line or triangle) through the screan,some part of the object will be covered.
as shown below
Here's my code.I don't know how should i change it.
Mouse scroll function: scroll_callback
the camera cover data: how to set the camera boundary
from math import pi
from threading import Thread
import glfw
import glm
import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import compileShader, compileProgram
gl_shape = {'lines': GL_LINES, 'line_strip': GL_LINE_STRIP, 'triangles': GL_TRIANGLES,
'triangle_strip': GL_TRIANGLE_STRIP}
gl_draw = {'seldom': GL_STATIC_DRAW, 'often': GL_DYNAMIC_DRAW, 'always': GL_STREAM_DRAW}
class MyOpenGL(Thread):
instances = {}
first_instance = {}
def __new__(cls, *args, **kwargs):
instance = kwargs.get('instance')
if instance not in cls.instances:
cls.instances[instance] = super().__new__(cls)
cls.first_instance[instance] = True # 判断是否是第一次创建
return cls.instances[instance]
def __init__(self, title: str = 'opengl_title', instance: str = None, draw_axis=True, axis_len=1.0):
super().__init__()
if not self.__class__.first_instance.get(instance): # 判断是否是第一次创建
return
self.__class__.first_instance[instance] = False
self.vertex_src = """
# version 330
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_color;
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;
out vec3 v_color;
void main()
{
gl_Position = projection * view * model * vec4(a_position, 1.0);
v_color = a_color;
}
""" # vec4(a_position, 1); 1指物体大小,越大越小
self.fragment_src = """
# version 330
in vec3 v_color;
out vec4 out_color;
void main()
{
out_color = vec4(v_color, 1.0);
}
"""
self.width = 1280
self.height = 720
self.eye = glm.vec3(0, 0, 3) # 眼睛的位置(默认z轴的正方向)
self.target = glm.vec3(0, 0, 0) # 瞄准方向的参考点(默认在坐标原点)
self.up = glm.vec3(0, 1, 0) # 定义对观察者而言的上方(默认y轴的正方向)
self.near = 0.1
self.far = 100
self.radians = glm.radians(45)
self.projection = glm.perspective(self.radians, self.width / self.height, self.near, self.far)
self.view = glm.lookAt(self.eye, self.target, self.up)
self.pan_start = None
self.orbit_start = None
self.pivot_world = None
self.draw_axis = draw_axis
self.axis_len = axis_len
self.title = title
self.window = None
self.program = None
self.vao = {}
self.to_bind_data = {}
self.vao_draw_args = {}
self.vao_index = 1
self.data_change = False
def glfw_init(self, title='opengl_title'):
if not glfw.init():
print("Cannot initialize GLFW")
exit()
self.window = glfw.create_window(self.width, self.height, title, None, None)
if not self.window:
glfw.terminate()
glfw.make_context_current(self.window)
def draw_axis_func(self, length=1.0):
axis = np.array([
length, 0.0, 0.0, 1.0, 0.0, 0.0,
-length, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, length, 0.0, 0.0, 1.0, 0.0,
0.0, -length, 0.0, 0.0, 1.0, 0.0,
0.0, 0.0, length, 0.0, 0.0, 1.0,
0.0, 0.0, -length, 0.0, 0.0, 1.0,
], np.float32)
self.vao['default_axis'] = glGenVertexArrays(1)
self.vao_draw_args['default_axis'] = glDrawArrays, (GL_LINES, 0, int(len(axis) / 6))
glBindVertexArray(self.vao['default_axis'])
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, axis.nbytes, axis, GL_STATIC_DRAW)
glEnableVertexAttribArray(0) # 准备设置glsl。即vertex_src中的layout(location = 0)的属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, axis.itemsize * 6, ctypes.c_void_p(0))
glEnableVertexAttribArray(1) # 准备设置glsl。即vertex_src中的layout(location = 1)的属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, axis.itemsize * 6, ctypes.c_void_p(12))
def depth(self, x, y):
depth_buffer = glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
depth_ = float(depth_buffer[0][0])
if depth_ == 1:
pt_drag = glm.vec3(0, 0, 0)
clip_pos = self.projection * self.view * glm.vec4(pt_drag, 1)
ndc_pos = glm.vec3(clip_pos) / clip_pos.w
if -1 < ndc_pos.z < 1:
depth_ = ndc_pos.z * 0.5 + 0.5
return depth_
# def update_fps_counter(self):
# """ 显示帧率 update_fps_counter(window)"""
# self.previous_seconds = self.__dict__.get('previous_seconds') or 0
# self.fps = self.__dict__.get('previous_seconds') or 0
# self.frame_count = self.__dict__.get('previous_seconds') or 0
# current_seconds = glfw.get_time()
# elapsed_seconds = current_seconds - self.previous_seconds
# if elapsed_seconds > 0.25:
# self.previous_seconds = current_seconds
# self.fps = self.frame_count / elapsed_seconds
# print(self.fps)
# self.frame_count = 0
# self.frame_count += 1
def window_size_callback(self, window, width, height):
glViewport(0, 0, width, height)
# 旋转速度随窗口变化而变化
self.width, self.height = width, height
self.projection = glm.perspective(self.radians, width / height, self.near, self.far)
def mouse_button_callback(self, window, button, action, mods):
x_pos, y_pos = glfw.get_cursor_pos(window)
y_pos = self.height - y_pos
if button == glfw.MOUSE_BUTTON_RIGHT and action == glfw.PRESS:
self.pan_start = glm.vec3(x_pos, y_pos, self.depth(x_pos, y_pos))
if button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS:
self.orbit_start = glm.vec3(x_pos, y_pos, self.depth(x_pos, y_pos))
self.pivot_world = glm.vec3(0, 0, 0)
def cursor_pos_callback(self, window, x_pos, y_pos):
y_pos = self.height - y_pos
# get view matrix and viewport rectangle
view, inv_view = self.view, glm.inverse(self.view)
view_rect = glm.vec4(0, 0, self.width, self.height)
if glfw.get_mouse_button(window, glfw.MOUSE_BUTTON_RIGHT) == glfw.PRESS:
# get drag start and end
wnd_from = self.pan_start
wnd_to = glm.vec3(x_pos, y_pos, self.pan_start[2])
self.pan_start = wnd_to
# get projection and window matrix
inv_proj = glm.inverse(self.projection)
inv_wnd = glm.translate(glm.mat4(1), glm.vec3(-1, -1, -1))
inv_wnd = glm.scale(inv_wnd, glm.vec3(2 / view_rect[2], 2 / view_rect[3], 2))
inv_wnd = glm.translate(inv_wnd, glm.vec3(view_rect[0], view_rect[1], 0))
# calculate drag start and world coordinates
pt_h_world = [inv_view * inv_proj * inv_wnd * glm.vec4(*pt, 1) for pt in [wnd_from, wnd_to]]
pt_world = [glm.vec3(pt_h) / pt_h.w for pt_h in pt_h_world]
# calculate drag world translation
world_vec = pt_world[1] - pt_world[0]
# translate view position and update view matrix
inv_view = glm.translate(glm.mat4(1), world_vec * -1) * inv_view
view = glm.inverse(inv_view)
elif glfw.get_mouse_button(window, glfw.MOUSE_BUTTON_LEFT) == glfw.PRESS:
# get the drag start and end
wnd_from = self.orbit_start
wnd_to = glm.vec3(x_pos, y_pos, self.orbit_start[2])
self.orbit_start = wnd_to
# calculate the pivot, rotation axis and angle
pivot_view = glm.vec3(view * glm.vec4(*self.pivot_world, 1))
orbit_dir = wnd_to - wnd_from
# get the projection of the up vector to the view port
# TODO
# calculate the rotation components for the rotation around the view space x axis and the world up vector
orbit_dir_x = glm.vec2(0, 1)
orbit_vec_x = glm.vec2(0, orbit_dir.y)
orbit_dir_up = glm.vec2(1, 0)
orbit_vec_up = glm.vec2(orbit_dir.x, 0)
# calculate the rotation matrix around the view space x axis through the pivot
rot_pivot_x = glm.mat4(1)
if glm.length(orbit_vec_x) > 0.5:
axis_x = glm.vec3(-1, 0, 0)
angle_x = glm.dot(orbit_dir_x, glm.vec2(orbit_vec_x.x / (view_rect[2] - view_rect[0]),
orbit_vec_x.y / (view_rect[3] - view_rect[1]))) * pi
rot_mat_x = glm.rotate(glm.mat4(1), angle_x, axis_x)
rot_pivot_x = glm.translate(glm.mat4(1), pivot_view) * rot_mat_x * glm.translate(glm.mat4(1),
-pivot_view)
# calculate the rotation matrix around the world space up vector through the pivot
rot_pivot_up = glm.mat4(1)
if glm.length(orbit_vec_up) > 0.5:
axis_up = glm.vec3(0, 0, 1)
angle_up = glm.dot(orbit_dir_up, glm.vec2(
orbit_vec_up.x / (view_rect[2] - view_rect[0]),
orbit_vec_up.y / (view_rect[3] - view_rect[1]))) * pi
rot_mat_up = glm.rotate(glm.mat4(1), angle_up, axis_up)
rot_pivot_up = glm.translate(
glm.mat4(1), self.pivot_world) * rot_mat_up * glm.translate(glm.mat4(1), -self.pivot_world)
# transform and update view matrix
view = rot_pivot_x * view * rot_pivot_up
self.view = view
def scroll_callback(self, window, x_offset, y_offset):
x, y = glfw.get_cursor_pos(window)
y = self.height - y
view_rect = glm.vec4(0, 0, self.width, self.height)
# get view, projection and window matrix
proj, inv_proj = self.projection, glm.inverse(self.projection)
view, inv_view = self.view, glm.inverse(self.view)
inv_wnd = glm.translate(glm.mat4(1), glm.vec3(-1, -1, -1))
inv_wnd = glm.scale(inv_wnd, glm.vec3(2 / view_rect[2], 2 / view_rect[3], 2))
inv_wnd = glm.translate(inv_wnd, glm.vec3(view_rect[0], view_rect[1], 0))
wnd = glm.inverse(inv_wnd)
# get world space position on view ray
pt_wnd = glm.vec3(x, y, 1.0)
# pt_world = glm.unProject(pt_wnd, view, proj, vp_rect)
pt_h_world = inv_view * inv_proj * inv_wnd * glm.vec4(*pt_wnd, 1)
pt_world = glm.vec3(pt_h_world) / pt_h_world.w
# get view position
eye = glm.vec3(inv_view[3])
# get "zoom" direction and amount
ray_cursor = glm.normalize(pt_world - eye)
# translate view position and update view matrix
inv_view = glm.translate(glm.mat4(1), ray_cursor * y_offset) * inv_view
# return new view matrix
self.view = glm.inverse(inv_view)
def set_data(self, data, data_name=None, frequency=None, shape=None, indices=None):
if data_name is None:
data_name = self.vao_index
self.vao_index += 1
# print(data_name)
self.to_bind_data[data_name] = (data, frequency, shape, indices)
return data_name
def bind_data(self):
if not self.to_bind_data:
return
for data_name in list(self.to_bind_data.keys()):
self.vao[data_name] = glGenVertexArrays(1)
data, frequency, shape, indices = self.to_bind_data[data_name]
glBindVertexArray(self.vao[data_name])
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, sum(len(i) for i in data) * 4, None, gl_draw.get(frequency, GL_STATIC_DRAW))
offset = 0
for d in data:
glBufferSubData(GL_ARRAY_BUFFER, offset, d.nbytes, d)
offset += d.nbytes
if indices is not None:
ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
length = sum(len(i) for i in indices) * 4
glBufferData(GL_ELEMENT_ARRAY_BUFFER, length, None, gl_draw.get(frequency, GL_STATIC_DRAW))
offset = 0
for i, sub_arr in enumerate(indices):
arr = (ctypes.c_uint32 * sub_arr.nbytes)(*(sub_arr + 4 * i))
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, sub_arr.nbytes, arr)
offset += sub_arr.nbytes
prop = glDrawElements, (gl_shape.get(shape, GL_LINE_STRIP), length, GL_UNSIGNED_INT, None)
else:
# 最后一个参数为画几个点
prop = glDrawArrays, (gl_shape.get(shape, GL_LINE_STRIP), 0, sum(int(len(d) / 6) for d in data))
self.vao_draw_args[data_name] = prop
glEnableVertexAttribArray(0) # 准备设置glsl。即vertex_src中的layout(location = 0)的属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4 * 6, ctypes.c_void_p(0))
glEnableVertexAttribArray(1) # 准备设置glsl。即vertex_src中的layout(location = 1)的属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 4 * 6, ctypes.c_void_p(12)) # 3*arr.itemsize
self.to_bind_data.pop(data_name)
def draw_data(self):
for n in self.vao:
glBindVertexArray(self.vao[n])
self.vao_draw_args[n][0](*self.vao_draw_args[n][1])
# print(self.vao_draw_args[n][0],self.vao_draw_args[n][1])
def run(self):
self.glfw_init(self.title)
if self.draw_axis:
self.draw_axis_func(self.axis_len)
glfw.set_window_size_callback(self.window, self.window_size_callback)
glfw.set_mouse_button_callback(self.window, self.mouse_button_callback)
glfw.set_cursor_pos_callback(self.window, self.cursor_pos_callback)
glfw.set_scroll_callback(self.window, self.scroll_callback)
self.program = compileProgram(compileShader(self.vertex_src, GL_VERTEX_SHADER),
compileShader(self.fragment_src, GL_FRAGMENT_SHADER))
glUseProgram(self.program)
# 设置模型坐标,和glUniformMatrix4fv不能写成一行,写成一行会无法展示,可能是因为用的指针地址
mat = glm.mat4()
glUniformMatrix4fv(glGetUniformLocation(self.program, 'model'), 1, GL_FALSE, glm.value_ptr(mat))
while not glfw.window_should_close(self.window):
glUniformMatrix4fv(glGetUniformLocation(self.program, 'projection'), 1, GL_FALSE,
glm.value_ptr(self.projection))
glUniformMatrix4fv(glGetUniformLocation(self.program, 'view'), 1, GL_FALSE, glm.value_ptr(self.view))
glClearColor(0.2, 0.3, 0.3, 1.0) # 在glClear后会显示为glClearColor中的颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
self.bind_data()
self.draw_data()
glfw.poll_events()
glfw.swap_buffers(self.window)
glfw.terminate()
print('glfw close')
result = [
np.array([0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 0.5, 0.4, 0.0, 1.0, 0.0, 0.0,
0.4, 0.4, 0.0, 1.0, 0.0, 0.0, 0.4, 0.5, 0.0, 1.0, 0.0, 0.0], dtype=np.float32),
np.array([-0.5, -0.5, 0.0, 0.0, 1.0, 0.0, -0.5, -0.4, 0.0, 0.0, 1.0, 0.0,
-0.4, -0.4, 0.0, 0.0, 1.0, 0.0, -0.4, -0.5, 0.0, 0.0, 1.0, 0.0], dtype=np.float32),
np.array([0.2, 0.2, 0.0, 0.0, 0.0, 1.0, 0.2, 0.1, 0.0, 0.0, 0.0, 1.0,
0.1, 0.1, 0.0, 0.0, 0.0, 1.0, 0.1, 0.2, 0.0, 0.0, 0.0, 1.0], dtype=np.float32)
]
ins = [np.array([2, 3, 1, 3, 0, 1], dtype=np.uint32), np.array([2, 3, 1, 3, 0, 1], dtype=np.uint32),
np.array([2, 3, 1, 3, 0, 1], dtype=np.uint32)]
line = [
np.array([0.1, 0.1, 0.1, 0.0, 1.0, 0.0, 0.2, 0.2, 0.2, 0.0, 1.0, 0.0,
0.3, 0.3, 0.3, 0.0, 1.0, 0.0, 0.4, 0.4, 0.4, 0.0, 1.0, 0.0,
0.5, 0.2, 0.3, 0.0, 1.0, 0.0, 0.5, 0.3, 0.4, 0.0, 1.0, 0.0,
0.6, 0.4, 0.5, 0.0, 1.0, 0.0, 0.6, 0.6, 0.6, 0.0, 1.0, 0.0], dtype=np.float32)
]
ogl = MyOpenGL()
ogl.start()
ogl.set_data(result, data_name=f'j', frequency='always', shape='triangles', indices=ins)
ogl.set_data(line, data_name=f'l', frequency='always')

Pygame + OpenGL - how to draw text after glBegin()?

I found somewhere on StackOverflow this cross-platform way to draw text:
def drawText(x, y, text):
position = (x, y, 0)
font = pygame.font.Font(None, 64)
textSurface = font.render(text, True, (255,255,255,255),
(0,0,0,255))
textData = pygame.image.tostring(textSurface, "RGBA", True)
GL.glRasterPos3d(*position)
GL.glDrawPixels(textSurface.get_width(), textSurface.get_height(),
GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, textData)
The problem is that I cannot call drawText after I called glBegin(GL_QUADS). How can I create a rectangle and texture it with the text's contents, then display it so that this drawText could be called even after glBegin?
I found that I can't change the current texture inside of glBegin, so I had to redesign parts of my code. Here's an example of using pygame to create the texture, based on Python version of Nehe's tutorial, lesson 6:
#!/usr/bin/env python
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import pygame
ESCAPE = '\033'
window = 0
texture = 0
A_TEX_NUMBER = None
B_TEX_NUMBER = None
def GenTextureForText(text):
font = pygame.font.Font(None, 64)
textSurface = font.render(text, True, (255,255,255,255),
(0,0,0,255))
ix, iy = textSurface.get_width(), textSurface.get_height()
image = pygame.image.tostring(textSurface, "RGBX", True)
glPixelStorei(GL_UNPACK_ALIGNMENT,1)
i = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, i)
glTexImage2D(GL_TEXTURE_2D, 0, 3, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
return i
def InitGL(Width, Height):
global A_TEX_NUMBER, B_TEX_NUMBER
pygame.init()
A_TEX_NUMBER = GenTextureForText("a")
B_TEX_NUMBER = GenTextureForText("b")
glEnable(GL_TEXTURE_2D)
glClearColor(0.0, 0.0, 0.0, 0.0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
done = 1
def DrawGLScene():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glTranslatef(0.0,0.0,-10.0)
glBindTexture(GL_TEXTURE_2D, B_TEX_NUMBER)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0)
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, 1.0)
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, 1.0)
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0)
glEnd()
glutSwapBuffers()
def keyPressed(*args):
if args[0] == ESCAPE:
glutDestroyWindow(window)
sys.exit()
def main():
global window
glutInit("")
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(640, 480)
glutInitWindowPosition(0, 0)
window = glutCreateWindow("Jeff Molofee's GL Code Tutorial ... NeHe '99")
glutDisplayFunc(DrawGLScene)
glutIdleFunc(DrawGLScene)
glutKeyboardFunc(keyPressed)
InitGL(640, 480)
glutMainLoop()
print "Hit ESC key to quit."
main()

Spot light shining on a flat surface

I want to shine a nice spotlight on a flat surface. I know that lighting is done per vertex and thus have create may vertices on the surface -- see this answer. However, I am getting these -- with GL_QUADS and GL_LINE_STRIP just to check that I have done things correctly.
These are clearly rather poor. so,
What need I chance so that the spotlight appears more like a circle on the surface?
How can I draw this scene faster?
Note: I realise that the normal calculation is not strictly necessary in this case but in a general case, it would be needed. Also, I could use a display list for the surface so it was only drawn once.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
from Numeric import *
lightPosition = np.array([10, 30, 20, 1])
view_rotation = np.array([0, 0, 0])
def init():
globAmb = [0.3, 0.3, 0.3, 1.0]
lightAmb = [0.0, 0.0, 0.0, 1.0]
lightDifAndSpec = [0.7, 0.7, 0.7, 1.0]
glutInit()
glClearColor(0.0, 0.0, 0.0, 0.0)
glEnable(GL_DEPTH_TEST)
glClearDepth(1.0)
glDepthFunc(GL_LESS)
glShadeModel(GL_SMOOTH)
glEnable(GL_LIGHTING)
glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb)
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDifAndSpec)
glLightfv(GL_LIGHT0, GL_SPECULAR, lightDifAndSpec)
glEnable(GL_LIGHT0)
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, globAmb)
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
def display():
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
gluLookAt(0.0, 40.0, 40.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
glRotatef(view_rotation[0], 1.0, 0.0, 0.0)
glRotatef(view_rotation[1], 0.0, 1.0, 0.0)
glRotatef(view_rotation[2], 0.0, 0.0, 1.0)
glPushMatrix()
pos = [0, 20, 0, 1]
direction = [0.0, -1.0, 0.0]
spotAngle = 20
glLightfv(GL_LIGHT0, GL_POSITION, pos)
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, spotAngle)
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction)
glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 2)
glPushMatrix();
glDisable(GL_LIGHTING)
glTranslate(pos[0], 0.5* pos[1], pos[2])
glRotatef(-90.0, 1.0, 0.0, 0.0)
glColor3f(1.0, 1.0, 1.0)
PI = 3.141592
glutWireCone(3.0 * np.tan( spotAngle/180.0 * PI ), pos[1], 10, 6)
glEnable(GL_LIGHTING)
glPopMatrix();
draw_cube()
glPopMatrix()
glFlush ()
def reshape(w, h):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45.0, float(w) / float(h), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
def keyboard(key, x, y):
if key == chr(27):
sys.exit(0)
elif key == 'w':
view_rotation[0] += 10
display()
elif key == 's':
view_rotation[0] -= 10
display()
elif key == 'a':
view_rotation[1] -= 10
display()
elif key == 'd':
view_rotation[1] += 10
display()
else:
print "Unknown %s key" %(key)
def draw_cube ():
glPushMatrix()
glRotatef(45, 0, 1, 0)
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, [183/256.0, 65/256.0, 14/256.0, 1.0]);
glMaterialfv(GL_FRONT, GL_SPECULAR, [1, 1, 1, 1]);
glMaterialfv(GL_FRONT, GL_SHININESS, [100.0]);
sz = 10
step = 1
for x in arange(-sz, sz, step):
for z in arange(-sz, sz, step):
v0 = np.array([x, sz, z])
v1 = np.array([x, sz, z+step])
v2 = np.array([x+step, sz, z+step])
v3 = np.array([x+step, sz, z])
#glBegin(GL_QUADS) # Uncomment to get the surface instead of lines.
glBegin(GL_LINE_STRIP)
n = get_normal_vector(v0, v1, v3)
glNormal(n[0], n[1], n[2])
glVertex3f(v0[0], v0[1], v0[2])
n = get_normal_vector(v1, v2, v0)
glNormal(n[0], n[1], n[2])
glVertex3f(v1[0], v1[1], v1[2])
n = get_normal_vector(v2, v3, v1)
glNormal(n[0], n[1], n[2])
glVertex3f(v2[0], v2[1], v2[2])
n = get_normal_vector(v3, v0, v2)
glNormal(n[0], n[1], n[2])
glVertex3f(v3[0], v3[1], v3[2])
glEnd()
glPopMatrix()
def get_normal_vector (v1, v2, v3):
v = np.cross(v2-v1, v3-v1)
n = np.sqrt(np.dot(v, v.conj()))
if n:
return v/n
else:
print v1
print v2
print v3
print v/n
sys.exit(-1)
glutInit(sys.argv)
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize(800, 800)
glutInitWindowPosition(300, 0)
glutCreateWindow('Lines')
init()
glutDisplayFunc(display)
glutReshapeFunc(reshape)
glutKeyboardFunc(keyboard)
glutMainLoop()
PS: I will update an answer with the source code using a shader when I have it working...
Use a fragment shader to render the spotlight. This is also the fastest way you can render the scene because you won't be increasing tessellation, yet get the highest quality lighting.
Hope this helps!

OpenGL Shadow Mapping using GLSL

I am trying to get shadow mapping working using GLSL. Unfortunately my depth render results are unusable even I have a pretty decent depth buffer precision. It is rendering like wireframe, following image may be a better description.
I am also including a test case(single file including shader), only dependency is pyopengl.
# shadow mapping test
# utkualtinkaya at gmail
# shader is from http://www.fabiensanglard.net/shadowmapping/index.php
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GL.shaders import *
from OpenGL.GL.framebufferobjects import *
import math
class Camera:
def __init__(self):
self.rotx, self.roty = math.pi/4, math.pi/4
self.distance = 100
self.moving = False
self.ex, self.ey = 0, 0
self.size = (800, 600)
def load_matrices(self):
glViewport(0, 0, *self.size)
y = math.cos(self.roty) * self.distance
x = math.sin(self.roty) * math.cos(self.rotx) * self.distance
z = math.sin(self.roty) * math.sin(self.rotx) * self.distance
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45.0, self.size[0]/float(self.size[1]), 1, 1000)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(x,y,z, 0,0,0, 0,1,0)
def on_mouse_button (self, b, s, x, y):
self.moving = not s
self.ex, self.ey = x, y
if b in [3, 4]:
dz = (1 if b == 3 else -1)
self.distance += self.distance/15.0 * dz;
def on_mouse_move(self, x, y, z = 0):
if self.moving:
self.rotx += (x-self.ex) / 300.0
self.roty += -(y-self.ey) / 300.0
self.ex, self.ey = x, y
def set_size(self, w, h):
self.size = w, h
class Shader():
def __init__(self):
self.is_built = False
self.uniforms = {}
def build(self):
self.program = compileProgram(
compileShader('''
uniform mat4 camMatrix;
uniform mat4 shadowMatrix;
varying vec4 depthProjection;
uniform bool useShadow;
void main() {
gl_Position = camMatrix * gl_ModelViewMatrix * gl_Vertex;
depthProjection = shadowMatrix * gl_ModelViewMatrix * gl_Vertex;
gl_FrontColor = gl_Color;
}
''',GL_VERTEX_SHADER),
compileShader('''
varying vec4 depthProjection;
uniform sampler2D shadowMap;
uniform bool useShadow;
void main () {
float shadow = 1.0;
if (useShadow) {
vec4 shadowCoord = depthProjection / depthProjection.w ;
// shadowCoord.z -= 0.0003;
float distanceFromLight = texture2D(shadowMap, shadowCoord.st).z;
if (depthProjection .w > 0.0)
shadow = distanceFromLight < shadowCoord.z ? 0.5 : 1.0 ;
}
gl_FragColor = shadow * gl_Color;
}
''',GL_FRAGMENT_SHADER),)
self.is_built = True
self.uniforms['camMatrix'] = glGetUniformLocation(self.program, 'camMatrix')
self.uniforms['shadowMatrix'] = glGetUniformLocation(self.program, 'shadowMatrix')
self.uniforms['shadowMap'] = glGetUniformLocation(self.program, 'shadowMap')
self.uniforms['useShadow'] = glGetUniformLocation(self.program, 'useShadow')
print self.uniforms
def use(self):
if not self.is_built:
self.build()
glUseProgram(self.program)
class Test:
def __init__(self):
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(800, 600)
glutInitWindowPosition(1120/2, 100)
self.window = glutCreateWindow("Shadow Test")
self.cam = Camera()
self.light = Camera()
self.cam.set_size(800, 600)
self.light.set_size(2048, 2048)
self.light.distance = 100
self.shader = Shader()
self.initialized = False
def setup(self):
self.initialized = True
glClearColor(0,0,0,1.0);
glDepthFunc(GL_LESS)
glEnable(GL_DEPTH_TEST)
self.fbo = glGenFramebuffers(1);
self.shadowTexture = glGenTextures(1)
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
w, h = self.light.size
glActiveTexture(GL_TEXTURE5)
glBindTexture(GL_TEXTURE_2D, self.shadowTexture)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, w, h, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, None)
glDrawBuffer(GL_NONE)
glReadBuffer(GL_NONE)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.fbo, 0)
FBOstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)
if FBOstatus != GL_FRAMEBUFFER_COMPLETE:
print ("GL_FRAMEBUFFER_COMPLETE_EXT failed, CANNOT use FBO\n");
glBindFramebuffer(GL_FRAMEBUFFER, 0)
#glActiveTexture(GL_TEXTURE0)
def draw(self):
glPushMatrix()
glTranslate(0, 10 ,0)
glColor4f(0, 1, 1, 1)
glutSolidCube(5)
glPopMatrix()
glPushMatrix()
glColor4f(0.5, 0.5, .5, 1)
glScale(100, 1, 100)
glutSolidCube(1)
glPopMatrix()
def apply_camera(self, cam):
cam.load_matrices()
model_view = glGetDoublev(GL_MODELVIEW_MATRIX);
projection = glGetDoublev(GL_PROJECTION_MATRIX);
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glMultMatrixd(projection)
glMultMatrixd(model_view)
glUniformMatrix4fv(self.shader.uniforms['camMatrix'], 1, False, glGetFloatv(GL_MODELVIEW_MATRIX))
glLoadIdentity()
def shadow_pass(self):
glUniform1i(self.shader.uniforms['useShadow'], 0)
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
glClear(GL_DEPTH_BUFFER_BIT)
glCullFace(GL_FRONT)
self.apply_camera(self.light)
self.draw()
glBindFramebuffer(GL_FRAMEBUFFER, 0)
def final_pass(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
self.light.load_matrices()
model_view = glGetDoublev(GL_MODELVIEW_MATRIX);
projection = glGetDoublev(GL_PROJECTION_MATRIX);
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
bias = [ 0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0]
glLoadMatrixd(bias)
glMultMatrixd(projection)
glMultMatrixd(model_view)
glUniformMatrix4fv(self.shader.uniforms['shadowMatrix'], 1, False, glGetFloatv(GL_MODELVIEW_MATRIX))
glActiveTexture(GL_TEXTURE5)
glBindTexture(GL_TEXTURE_2D, self.shadowTexture)
glUniform1i(self.shader.uniforms['shadowMap'], 5)
glUniform1i(self.shader.uniforms['useShadow'], 1);
self.apply_camera(self.cam)
glLoadIdentity()
glCullFace(GL_BACK)
self.draw()
def render(self):
if not self.initialized: self.setup()
self.shader.use()
self.shadow_pass()
self.final_pass()
glutSwapBuffers()
def mouse_move(self, *args):
self.cam.on_mouse_move(*args)
self.light.on_mouse_move(*args)
def mouse_button(self, b, *args):
if b==0:
self.light.on_mouse_button(b, *args)
else:
self.cam.on_mouse_button(b, *args)
def main(self):
glutDisplayFunc(self.render)
glutIdleFunc(self.render)
glutMouseFunc(self.mouse_button)
glutMotionFunc(self.mouse_move)
glutReshapeFunc(self.cam.set_size)
#self.setup()
glutMainLoop()
if __name__ == '__main__':
test = Test()
test.main()
solved it, it is the binding issue, in shadow pass fragment shader I simply checked a boolean value to disable reading the texture, but that was not enough. I should have unbind the texture before shadow pass, that is mentioned in documentation as:
Quote from OpenGL Refence:
Special precautions need to be taken to avoid attaching a texture image to the currently bound framebuffer while the texture object is currently bound and potentially sampled by the current vertex or fragment shader.
NVIDIA ignores this, ATI behaves pretty much "undefined" as the documentation says.
// shadowCoord.z -= 0.0003;
Your commented out shadow bias is the solution to your z-fighting problem. Change it to some value e.g. += 0.0005 and you should be good to go.

Categories