0
$\begingroup$

I have defined a Blender class to help me abstract out details for rendering, object manipulation etc. I setup my scene using this class and put 12 cameras in it. Then I start rendering objects by switching between each of these cameras. A very strange thing that I noticed was no matter what the camera coordinates are, the last camera ALWAYS renders a black image as I had mentioned before, although my objects are all located at (0, 0, 0). I did some trial and error and found out that adding a "fake" camera would resolve the issue. However, what I then noticed was the rendering results of camera00 and camera04 are ALWAYS the same although the location of both cameras are completely different. This happens even if I change all of the camera coordinates. Another other strange fact is that all other cameras render the right images. Initially I was thinking the camera coordinates are the same but when I loaded the .blend file on my laptop I noticed that is not the case. I can render the objects correctly if I manually load the blend file and render with camera04. However I cannot do my rendering like that as I have many objects. The way I am currently doing my renderings is by compiling Blender as a Python module and loading it on my machine's Python.

This is a very strange issue. Does anyone know whether I am doing something wrong or how I can resolve it? Am I activating the cameras properly?

Below you can see the class I have defined and how I do the rendering:

#blenderClass.py
import importlib

class Blender(object):
    def __init__(self):
        self.bpy = importlib.import_module("bpy")
        self.scene = self.bpy.context.scene

    def setupScene(self, camPosList):
        self.cleanScene()
        self.addSun()

        self.numCams = len(camPosList)
        for i, coords in enumerate(camPosList):
            self.bpy.ops.object.camera_add(location=tuple(coords))
            self.activeObj = self.scene.objects.active
            self.activeObj.name = 'camera.0' + str(i)
            self.activeObj.data.name = 'camera.0' + str(i)
            self.pointObjTo(self.activeObj, (0.0, 0.0, 0.0))

        # Add a fake camera but never activate it
        self.bpy.ops.object.camera_add(location=tuple(coords)) # Use the last coords
        self.activeObj = self.scene.objects.active
        self.activeObj.name = 'camera.0' + str(i+1) # Use the last i from the for loop
        self.activeObj.data.name = 'camera.0' + str(i+1)
        self.pointObjTo(self.activeObj, (0.0, 0.0, 0.0))

   def render(self, objPath, renderPath):
        self.bpy.ops.import_scene.obj(filepath=objPath, split_mode="OFF")
        for i in range(self.numCams):
            self.scene.render.filepath =  renderPath + str(i) + '.png'
            self.activateCamera(i)
            self.bpy.ops.render.render(write_still=True)

   def pointObjTo(self, obj, xyzTarget):
        direction = Vector(xyzTarget) - obj.location
        obj.rotation_euler = direction.to_track_quat('-Z', 'Y').to_euler()

   def activateCamera(self, camNo):
        self.scene.camera = self.scene.objects['camera.0' + str(camNo)]
        self.scene.update()

   def addSun(self, location=(0.0, 0.0, 1)):
        self.bpy.ops.object.lamp_add(type='SUN', location=location)
        self.activeObj = self.scene.objects.active
        self.activeObj.name = 'interiorIlluminator'
        self.activeObj.data.name = 'interiorIlluminator'
        self.activeObj.data.shadow_method = 'NOSHADOW'
        self.activeObj.data.energy = 0.8


    def cleanScene(self):
        self.removeLamps()
        self.removeMeshes(0)
        self.removeCameras()

    def removeMeshes(self, layer = -1):
        for obj in self.scene.objects:
            if obj.type == 'MESH' and obj.layers[layer != -1 and layer]:
                obj.select = True
            else:
                obj.select = False
        self.bpy.ops.object.delete()


    def removeCameras(self):
        for obj in self.scene.objects:
            if obj.type == 'CAMERA':
                obj.select = True
            else:
                obj.select = False
        self.bpy.ops.object.delete()


    def removeLamps(self):
        for obj in self.scene.objects:
            if obj.type == 'LAMP':
                obj.select = True
            else:
                obj.select = False
        self.bpy.ops.object.delete()

And here's how I do my renderings:

#main.py
import blenderClass import Blender
import numpy as np
camPosList = np.loadtxt('camPosList.txt')
blender = Blender()
blender.setupScene()
objPaths = ['obj1.obj', 'obj2.obj', 'obj3.obj', 'obj4.obj']

for objPath in objPaths:
     blender.render(objPath, 'somePath')

And here are the coordinates in camPosList::

1.106609463691711426e+00 -1.284419745206832886e-01 -4.973111748695373535e-01
2.114785015583038330e-01 -1.041638731956481934e+00 -5.988617539405822754e-01
6.345187127590179443e-02 7.305476069450378418e-02 -1.216143369674682617e+00
5.116637349128723145e-01 1.007419824600219727e+00 -4.601101875305175781e-01
9.366978406906127930e-01 4.701653420925140381e-01 6.244274377822875977e-01
7.511696219444274902e-01 -7.962130308151245117e-01 5.386640429496765137e-01
-9.366978406906127930e-01 -4.701653420925140381e-01 -6.244274377822875977e-01
-7.511696219444274902e-01 7.962130308151245117e-01 -5.386640429496765137e-01
-2.114785015583038330e-01 1.041638731956481934e+00 5.988617539405822754e-01
-6.345187127590179443e-02 -7.305476069450378418e-02 1.216143369674682617e+00
-5.116637349128723145e-01 -1.007419824600219727e+00 4.601101875305175781e-01
-1.106609463691711426e+00 1.284419745206832886e-01 4.973111748695373535e-01

You can download a set of rendering results from here: https://drive.google.com/open?id=1vqX_lLCFY7W3gll5CJ2hoaNzmhK8BLxp

And finally a .Blende file:

$\endgroup$
  • $\begingroup$ I notice that the “activateCamera(self, camNo)” function assigns self.scene.camera twice - firstly to self.scene.objects['camera.0' + str(camNo)] and secondly to self.activeObj. Why is that? Without understanding more about this assignment I can’t say if this would be a problem but it certainly looks suspicious. $\endgroup$ – Rich Sedman May 8 '18 at 13:25
  • $\begingroup$ @RichSedman Sorry that was a mistake. I don't use self.activeObj for that at all. Updated my post $\endgroup$ – Amir May 8 '18 at 13:26
  • 6
    $\begingroup$ Does the guy who solves your issue get a credit in the paper? $\endgroup$ – brockmann May 8 '18 at 15:03
  • $\begingroup$ @brockmann Yes; he/she will be acknowledged :) $\endgroup$ – Amir May 8 '18 at 20:53
  • $\begingroup$ @RichSedman I resolved the issue in a hacky way. But do you think any part of my original code could be culprit for this problem? $\endgroup$ – Amir May 9 '18 at 0:04
2
$\begingroup$

This is a very weird solution but adding another fake camera before adding camera.00 and doing the renderings from camera.00 to camera.11 resolves the issue! I'm not sure whether this is a bug or not but to me it certainly looks like a bug and I will report it.

$\endgroup$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.