2
$\begingroup$

I need to code my own Render Engine to do specific rendering passes. I don't want to build my own Blender app by coding in c/c++, so i try to make it work in Python.

But i need OpenGL functions.

Inside the render function, I call bgl functions to use OpenGL. This allows me to compute the render of the scene in a fast way and get the result from OpenGL to display it on the Blender editor.

CustomRenderEngine.py :

import bpy
import math
from mathutils import * 
from bgl import * 
import time

class CustomRenderEngine(bpy.types.RenderEngine):

    bl_idname = "custom_renderer"
    bl_label = "Custom Renderer"
    use_preview = True

    def render(self, scene):

        scale = scene.render.resolution_percentage / 100.0
        width = int(scene.render.resolution_x * scale)
        height = int(scene.render.resolution_y * scale)

        # Get mesh objects and cameras 
        camera_objects = []
        mesh_objects = []
        for obj in scene.objects:
            print("\tName:%s \tType:%s"%(obj.name,obj.type))
            if obj.type == 'MESH':
                mesh_objects.append(obj)
            elif obj.type == 'CAMERA':
                camera_objects.append(obj)


        if len(camera_objects) == 0:
            print("No camera found.")
            return


        # Ideally, we could render for each camera in the scene
        camera = camera_objects[0]
        z_near = camera.data.clip_start
        z_far = camera.data.clip_end
        lens = camera.data.lens
        factor = lens / 32.0
        pr = float(width) / float(height)
        fovx = math.atan(.5/factor) 
        fovy = math.atan(.5/pr/factor)
        fov = fovy * 360 /math.pi   

        modelview_matrix = camera.matrix_world.inverted()

        # OpenGL init
        glClearColor( 1.0,1.0,1.0,1.0)
        glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        glEnable(GL_DEPTH_TEST)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glViewport(0,0,width,height)
        gluPerspective(fov, pr, z_near, z_far)
        glMatrixMode( GL_MODELVIEW )
        glLoadIdentity()
        glShadeModel( GL_SMOOTH)

        for obj in mesh_objects:
            print("Handle Object : %s"%obj.name)
            mesh = obj.data
            diffuse_color = mesh.materials[0].diffuse_color
            print("Diffuse color: %s"%diffuse_color)

            for polygon in mesh.polygons:
                glBegin(GL_POLYGON)
                for vertex_id in polygon.vertices:

                    # This is quite simple for now, only diffuse color and geometry
                    vertex = mesh.vertices[vertex_id]
                    v = modelview_matrix * obj.matrix_local * vertex.co
                    glColor3f(diffuse_color[0],diffuse_color[1],diffuse_color[2])
                    glVertex3f(v[0],v[1],v[2])
                glEnd()

        # Get the result from Open GL
        colBuff = Buffer(GL_FLOAT, (height,width,3))
        glReadPixels(0, 0, width, height, GL_RGB, GL_FLOAT,colBuff ) 

        # Write the result to Blender preview
        output_pixels = []
        for y in range (0,height):
            for x in range(0,width):
                col = colBuff[y][x]
                output_pixels.append([col[0],col[1],col[2],1.0])


        result = self.begin_result(0, 0, width, height)
        layer = result.layers[0].passes["Combined"]
        layer.rect = output_pixels
        self.end_result(result)


def register() :
    bpy.utils.register_class(CustomRenderEngine)

    from bl_ui import (properties_render)
    properties_render.RENDER_PT_render.COMPAT_ENGINES.add(CustomRenderEngine.bl_idname)

def unregister() :
    bpy.utils.unregister_class(CustomRenderEngine)

    from bl_ui import (properties_render)
    properties_render.RENDER_PT_render.COMPAT_ENGINES.remove(CustomRenderEngine.bl_idname)

__ init __.py

bl_info = {
    'name': 'CustomEngineTest',
    'category': 'SVG Render',
    'version': (0, 0, 1),
    'blender': (2, 78, 0)
}

modulesNames = ['CustomRenderEngine']

import sys
import importlib

modulesFullNames = {}
for currentModuleName in modulesNames:
    if 'DEBUG_MODE' in sys.argv:
        modulesFullNames[currentModuleName] = ('{}'.format(currentModuleName))
    else:
        modulesFullNames[currentModuleName] = ('{}.{}'.format(__name__, currentModuleName))

for currentModuleFullName in modulesFullNames.values():
    if currentModuleFullName in sys.modules:
        importlib.reload(sys.modules[currentModuleFullName])
    else:
        globals()[currentModuleFullName] = importlib.import_module(currentModuleFullName)
        setattr(globals()[currentModuleFullName], 'modulesNames', modulesFullNames)

def register():
    for currentModuleName in modulesFullNames.values():
        if currentModuleName in sys.modules:
            if hasattr(sys.modules[currentModuleName], 'register'):
                sys.modules[currentModuleName].register()

def unregister():
    for currentModuleName in modulesFullNames.values():
        if currentModuleName in sys.modules:
            if hasattr(sys.modules[currentModuleName], 'unregister'):
                sys.modules[currentModuleName].unregister()

if __name__ == "__main__":
    register()

When I started Blender from command line and call render with a script (start_addon.py), bgl function calls work as expected.

For example :

Scene : Scene

Result : Result

start_addon.py

import os
import sys
import bpy
from bgl import * 


filesDir = os.getcwd()

initFile = "__init__.py"

if filesDir not in sys.path:
    sys.path.append(filesDir)

file = os.path.join(filesDir, initFile)

if 'DEBUG_MODE' not in sys.argv:
    sys.argv.append('DEBUG_MODE')

exec(compile(open(file).read(), initFile, 'exec'))

if 'DEBUG_MODE' in sys.argv:
    sys.argv.remove('DEBUG_MODE')

bpy.context.scene.render.engine = "custom_renderer"
bpy.ops.render.render()

But when I manually set the render engine inside Blender UI and press "Render" Button, i got a segmentation fault with this backtrace :

# Blender 2.79 (sub 0), Commit date: 2017-09-11 10:43, Hash 5bd8ac9

# backtrace

0   blender   

                          0x0000000100c32b1a BLI_system_backtrace + 58

1   blender                             0x000000010013ee1a sig_handle_crash + 362

2   libsystem_platform.dylib            0x00007fffc571fb3a _sigtramp + 26

3   blender                             0x0000000102293ea6 PyObject_CallMethodObjArgs + 406

4   blender                             0x000000010059861c Method_ClearColor + 76

5   blender                             0x00000001022d93f8 PyCFunction_Call + 280

6   blender                             0x00000001023540f2 PyEval_EvalFrameEx + 26210

7   blender                             0x000000010235853f fast_function + 223

8   blender                             0x0000000102353bbb PyEval_EvalFrameEx + 24875

9   blender                             0x0000000102357cc5 _PyEval_EvalCodeWithName + 2421

10  blender                             0x000000010234da6e PyEval_EvalCodeEx + 78

11  blender                             0x00000001022bb7ad function_call + 381

12  blender                             0x00000001022935c0 PyObject_Call + 96

13  blender                             0x000000010057650d bpy_class_call + 1069

14  blender                             0x0000000100b3e851 engine_render + 113

15  blender                             0x00000001004f982b RE_engine_render + 971

16  blender                             0x000000010050f190 do_render_3d + 48

17  blender                             0x000000010050d8a1 do_render_fields_blur_3d + 289

18  blender                             0x000000010050c984 do_render_all_options + 388

19  blender                             0x000000010050c42e RE_BlenderFrame + 206

20  blender                             0x000000010044786b render_startjob + 139

21  blender                             0x0000000100150c2f do_job_thread + 31

22  libsystem_pthread.dylib             0x00007fffc572993b _pthread_body + 180

23  libsystem_pthread.dylib             0x00007fffc5729887 _pthread_body + 0

24  libsystem_pthread.dylib             0x00007fffc572908d thread_start + 13

It crashes on macOS high Sierra and also Sierra. Any ideas ?

Edit : Weirdest thing, if I tape in Python console bpy.ops.render.render() it works. But the Render button, which should call bpy.ops.render.render() finishes with the seg fault.

Thx in advance,

$\endgroup$
  • $\begingroup$ Rleated Can you please expand and correct some errors , eg bl_idname is lower case "customRender", and how have you defined mesh_objects. Suggest putting in the import bpy (and bgl) and register methods for class too. Makes it easier for those interested to test, and hence answer. (even though it breaks question rules somewhat) $\endgroup$ – batFINGER Oct 10 '17 at 10:38
  • $\begingroup$ Just Updated the post. $\endgroup$ – minitoine Oct 10 '17 at 11:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.