Updated gpu.types.GPUOffscreen.py example file
The image I get is a bit too dark, which is the same I get in the viewport itself when there is no Display Device. So I believe for cases like this we need to have color management on top of the buffer. Also, on EEVEE it looks like it needs to clear some extra buffers. You can see that, by rotating the camera view around. That said, this is the first step to bring back fun addons that use external offscreen buffers. Note: When using gpu offscreen with POST_VIEW (as oppose to POST_PIXEL) I get a crash with DST having 0xFF..
This commit is contained in:
parent
5340e9ac1b
commit
fbc837b2f8
|
@ -1,16 +1,58 @@
|
|||
# Draws an off-screen buffer and display it in the corner of the view.
|
||||
import bpy
|
||||
from bgl import *
|
||||
import bgl
|
||||
import gpu
|
||||
import numpy as np
|
||||
|
||||
g_imageVertSrc = '''
|
||||
in vec2 texCoord;
|
||||
in vec2 pos;
|
||||
|
||||
out vec2 texCoord_interp;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(pos.xy, 0.0f, 1.0);
|
||||
gl_Position.z = 1.0f;
|
||||
texCoord_interp = texCoord;
|
||||
}
|
||||
'''
|
||||
|
||||
g_imageFragSrc = '''
|
||||
in vec2 texCoord_interp;
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D image;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = texture(image, texCoord_interp);
|
||||
}
|
||||
'''
|
||||
|
||||
g_plane_vertices = np.array([
|
||||
([-1.0, -1.0], [0.0, 0.0]),
|
||||
([1.0, -1.0], [1.0, 0.0]),
|
||||
([1.0, 1.0], [1.0, 1.0]),
|
||||
([1.0, 1.0], [1.0, 1.0]),
|
||||
([-1.0, 1.0], [0.0, 1.0]),
|
||||
([-1.0, -1.0], [0.0, 0.0]),
|
||||
], [('pos', 'f4', 2), ('uv', 'f4', 2)])
|
||||
|
||||
|
||||
class OffScreenDraw(bpy.types.Operator):
|
||||
class VIEW3D_OT_draw_offscreen(bpy.types.Operator):
|
||||
bl_idname = "view3d.offscreen_draw"
|
||||
bl_label = "View3D Offscreen Draw"
|
||||
bl_label = "Viewport Offscreen Draw"
|
||||
|
||||
_handle_calc = None
|
||||
_handle_draw = None
|
||||
is_enabled = False
|
||||
|
||||
global_shader = None
|
||||
batch_plane = None
|
||||
uniform_image = -1
|
||||
shader = None
|
||||
|
||||
# manage draw handler
|
||||
@staticmethod
|
||||
def draw_callback_px(self, context):
|
||||
|
@ -22,22 +64,21 @@ class OffScreenDraw(bpy.types.Operator):
|
|||
|
||||
@staticmethod
|
||||
def handle_add(self, context):
|
||||
OffScreenDraw._handle_draw = bpy.types.SpaceView3D.draw_handler_add(
|
||||
VIEW3D_OT_draw_offscreen._handle_draw = bpy.types.SpaceView3D.draw_handler_add(
|
||||
self.draw_callback_px, (self, context),
|
||||
'WINDOW', 'POST_PIXEL',
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def handle_remove():
|
||||
if OffScreenDraw._handle_draw is not None:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(OffScreenDraw._handle_draw, 'WINDOW')
|
||||
if VIEW3D_OT_draw_offscreen._handle_draw is not None:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(VIEW3D_OT_draw_offscreen._handle_draw, 'WINDOW')
|
||||
|
||||
OffScreenDraw._handle_draw = None
|
||||
VIEW3D_OT_draw_offscreen._handle_draw = None
|
||||
|
||||
# off-screen buffer
|
||||
@staticmethod
|
||||
def _setup_offscreen(context):
|
||||
import gpu
|
||||
scene = context.scene
|
||||
aspect_ratio = scene.render.resolution_x / scene.render.resolution_y
|
||||
|
||||
|
@ -58,86 +99,96 @@ class OffScreenDraw(bpy.types.Operator):
|
|||
|
||||
modelview_matrix = camera.matrix_world.inverted()
|
||||
projection_matrix = camera.calc_matrix_camera(
|
||||
render.resolution_x,
|
||||
render.resolution_y,
|
||||
render.pixel_aspect_x,
|
||||
render.pixel_aspect_y,
|
||||
context.depsgraph,
|
||||
x=render.resolution_x,
|
||||
y=render.resolution_y,
|
||||
scale_x=render.pixel_aspect_x,
|
||||
scale_y=render.pixel_aspect_y,
|
||||
)
|
||||
|
||||
offscreen.draw_view3d(
|
||||
scene,
|
||||
render_layer,
|
||||
view_layer,
|
||||
context.space_data,
|
||||
context.region,
|
||||
projection_matrix,
|
||||
modelview_matrix,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _opengl_draw(context, texture, aspect_ratio, scale):
|
||||
def _opengl_draw(self, context, texture, aspect_ratio, scale):
|
||||
"""
|
||||
OpenGL code to draw a rectangle in the viewport
|
||||
"""
|
||||
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
|
||||
# view setup
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPushMatrix()
|
||||
glLoadIdentity()
|
||||
bgl.glDisable(bgl.GL_DEPTH_TEST)
|
||||
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPushMatrix()
|
||||
glLoadIdentity()
|
||||
viewport = bgl.Buffer(bgl.GL_INT, 4)
|
||||
bgl.glGetIntegerv(bgl.GL_VIEWPORT, viewport)
|
||||
|
||||
glOrtho(-1, 1, -1, 1, -15, 15)
|
||||
gluLookAt(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
|
||||
|
||||
act_tex = Buffer(GL_INT, 1)
|
||||
glGetIntegerv(GL_TEXTURE_2D, act_tex)
|
||||
|
||||
viewport = Buffer(GL_INT, 4)
|
||||
glGetIntegerv(GL_VIEWPORT, viewport)
|
||||
active_texture = bgl.Buffer(bgl.GL_INT, 1)
|
||||
bgl.glGetIntegerv(bgl.GL_TEXTURE_2D, active_texture)
|
||||
|
||||
width = int(scale * viewport[2])
|
||||
height = int(width / aspect_ratio)
|
||||
|
||||
glViewport(viewport[0], viewport[1], width, height)
|
||||
glScissor(viewport[0], viewport[1], width, height)
|
||||
bgl.glViewport(viewport[0], viewport[1], width, height)
|
||||
bgl.glScissor(viewport[0], viewport[1], width, height)
|
||||
|
||||
# draw routine
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glActiveTexture(GL_TEXTURE0)
|
||||
batch_plane = self.get_batch_plane()
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture)
|
||||
shader = VIEW3D_OT_draw_offscreen.shader
|
||||
# bind it so we can pass the new uniform values
|
||||
shader.bind()
|
||||
|
||||
texco = [(1, 1), (0, 1), (0, 0), (1, 0)]
|
||||
verco = [(1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0), (1.0, -1.0)]
|
||||
bgl.glEnable(bgl.GL_TEXTURE_2D)
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture)
|
||||
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
|
||||
|
||||
glColor4f(1.0, 1.0, 1.0, 1.0)
|
||||
|
||||
glBegin(GL_QUADS)
|
||||
for i in range(4):
|
||||
glTexCoord3f(texco[i][0], texco[i][1], 0.0)
|
||||
glVertex2f(verco[i][0], verco[i][1])
|
||||
glEnd()
|
||||
shader.uniform_int(VIEW3D_OT_draw_offscreen.uniform_image, 0)
|
||||
batch_plane.draw()
|
||||
|
||||
# restoring settings
|
||||
glBindTexture(GL_TEXTURE_2D, act_tex[0])
|
||||
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, active_texture[0])
|
||||
bgl.glDisable(bgl.GL_TEXTURE_2D)
|
||||
|
||||
# reset view
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPopMatrix()
|
||||
bgl.glViewport(viewport[0], viewport[1], viewport[2], viewport[3])
|
||||
bgl.glScissor(viewport[0], viewport[1], viewport[2], viewport[3])
|
||||
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPopMatrix()
|
||||
def get_batch_plane(self):
|
||||
if self.batch_plane is None:
|
||||
global g_plane_vertices
|
||||
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3])
|
||||
glScissor(viewport[0], viewport[1], viewport[2], viewport[3])
|
||||
format = gpu.types.GPUVertFormat()
|
||||
pos_id = format.attr_add(
|
||||
id="pos",
|
||||
comp_type="F32",
|
||||
len=2,
|
||||
fetch_mode="FLOAT")
|
||||
|
||||
uv_id = format.attr_add(
|
||||
id="texCoord",
|
||||
comp_type="F32",
|
||||
len=2,
|
||||
fetch_mode="FLOAT")
|
||||
|
||||
vbo = gpu.types.GPUVertBuf(
|
||||
len=len(g_plane_vertices),
|
||||
format=format)
|
||||
|
||||
vbo.fill(id=pos_id, data=g_plane_vertices["pos"])
|
||||
vbo.fill(id=uv_id, data=g_plane_vertices["uv"])
|
||||
|
||||
batch_plane = gpu.types.GPUBatch(type="TRIS", buf=vbo)
|
||||
shader = self.global_shader
|
||||
|
||||
VIEW3D_OT_draw_offscreen.shader = shader
|
||||
VIEW3D_OT_draw_offscreen.uniform_image = shader.uniform_from_name("image")
|
||||
|
||||
batch_plane.program_set(shader)
|
||||
VIEW3D_OT_draw_offscreen.batch_plane = batch_plane
|
||||
return VIEW3D_OT_draw_offscreen.batch_plane
|
||||
|
||||
# operator functions
|
||||
@classmethod
|
||||
|
@ -148,24 +199,26 @@ class OffScreenDraw(bpy.types.Operator):
|
|||
if context.area:
|
||||
context.area.tag_redraw()
|
||||
|
||||
if event.type in {'RIGHTMOUSE', 'ESC'}:
|
||||
self.cancel(context)
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'PASS_THROUGH'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
if OffScreenDraw.is_enabled:
|
||||
if VIEW3D_OT_draw_offscreen.is_enabled:
|
||||
self.cancel(context)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
else:
|
||||
self._offscreen = OffScreenDraw._setup_offscreen(context)
|
||||
self._offscreen = VIEW3D_OT_draw_offscreen._setup_offscreen(context)
|
||||
if self._offscreen:
|
||||
self._texture = self._offscreen.color_texture
|
||||
else:
|
||||
self.report({'ERROR'}, "Error initializing offscreen buffer. More details in the console")
|
||||
return {'CANCELLED'}
|
||||
|
||||
OffScreenDraw.handle_add(self, context)
|
||||
OffScreenDraw.is_enabled = True
|
||||
VIEW3D_OT_draw_offscreen.handle_add(self, context)
|
||||
VIEW3D_OT_draw_offscreen.is_enabled = True
|
||||
|
||||
if context.area:
|
||||
context.area.tag_redraw()
|
||||
|
@ -174,20 +227,38 @@ class OffScreenDraw(bpy.types.Operator):
|
|||
return {'RUNNING_MODAL'}
|
||||
|
||||
def cancel(self, context):
|
||||
OffScreenDraw.handle_remove()
|
||||
OffScreenDraw.is_enabled = False
|
||||
VIEW3D_OT_draw_offscreen.handle_remove()
|
||||
VIEW3D_OT_draw_offscreen.is_enabled = False
|
||||
|
||||
if VIEW3D_OT_draw_offscreen.batch_plane is not None:
|
||||
del VIEW3D_OT_draw_offscreen.batch_plane
|
||||
VIEW3D_OT_draw_offscreen.batch_plane = None
|
||||
|
||||
VIEW3D_OT_draw_offscreen.shader = None
|
||||
|
||||
if context.area:
|
||||
context.area.tag_redraw()
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(OffScreenDraw)
|
||||
if hasattr(bpy.types, "VIEW3D_OT_draw_offscreen"):
|
||||
del VIEW3D_OT_draw_offscreen.global_shader
|
||||
|
||||
shader = gpu.types.GPUShader(g_imageVertSrc, g_imageFragSrc)
|
||||
VIEW3D_OT_draw_offscreen.global_shader = shader
|
||||
|
||||
bpy.utils.register_class(VIEW3D_OT_draw_offscreen)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(OffScreenDraw)
|
||||
bpy.utils.unregister_class(VIEW3D_OT_draw_offscreen)
|
||||
VIEW3D_OT_draw_offscreen.global_shader = None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
unregister()
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
register()
|
||||
|
|
Loading…
Reference in New Issue