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:
Dalai Felinto 2018-09-20 22:06:51 +00:00
parent 5340e9ac1b
commit fbc837b2f8
1 changed files with 138 additions and 67 deletions

View File

@ -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()