Blender cameras convention I would like to create cameras looking at origin uniformly distributed in a sphere. I got most of it right...but the cameras are looking away from the origin. I know I can get it right by a certain combination of rotations, but I want to be sure that what I am doing is 100% correct. Can someone familiar with this help me point out what's the right way to do it?
By changing the lookat slightly in the &quot;wrong&quot; way I can get what is expected result. However, what I would like to do is apply the coordinate transform matrix to the camera matrix after the &quot;right&quot; lookat is computed(right lookat==x-right,y-down,z-forward,i.e. the cv convention).
There is an additional problem with the code that looks right. The camera scale is negated for some reason. I thought scale is not affected by transform matrix. Having negative scale causes some other problem when I am converting the camera back to cv work.
looks wrong but should be correct?

looks right but wrong.

code to create cameras
import bpy
import bmesh
import random
import math
import numpy as np

from mathutils import Matrix

def sphere_golden_spiral(num_pts=1000):
    from numpy import pi, cos, sin, arccos, arange
    indices = arange(0, num_pts, dtype=float) + 0.5
    phi = arccos(1 - 2*indices/num_pts)
    theta = pi * (1 + 5**0.5) * indices
    x, y, z = cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi)
    points = np.stack((x,y,z),axis=-1)
    return points

def norm(v):
    return v/np.linalg.norm(v)

def lookat(eye,at=np.array([0.,0,0]),up=np.array([0.,1,0])):
    z = norm(eye-at)
    x = norm(np.cross(z,up))
    y = np.cross(x,z)
    z = -z
    view_mat = np.zeros((4, 4))
    t =np.array([0.,0,0])
    t[0] = -np.dot(x,eye)
    t[1] = -np.dot(y,eye)
    t[2] = -np.dot(z,eye)
    view_mat[0,:3]=x
    view_mat[1,:3]=y
    view_mat[2,:3]=z    
    view_mat[:3,3]=t    
    view_mat[3,3]=1
    return np.linalg.inv(view_mat)

def rotx(a):
    from math import cos,sin
    return np.array([[1,0,0,0],
                     [0,cos(a),-sin(a),0],
                     [0,sin(a),cos(a),0],
                     [0,0,0,1]])   

def roty(a):
    from math import cos,sin
    return np.array([[cos(a),0,sin(a),0],
                     [0,1,0,0],
                     [-sin(a),0,cos(a),0],
                     [0,0,0,1]])   
                     
def rotz(a):
    from math import cos,sin
    return np.array([[cos(a),-sin(a),0],
                     [sin(a),cos(a),0,0],
                     [0,0,1,0],
                     [0,0,0,1]])   
                     
def cams_sphere(points):
    points = np.array(sphere_golden_spiral(points))
    group = bpy.data.groups.new('Trg_sphere')
    context = bpy.context
    for i in range(len(points)):
        p = points[i]
        bpy.ops.object.camera_add()
        cam = context.object
        cam.name='Traj_sphere_%i'%i
        transform = lookat(p*10)
        #print(rotx(math.radians(-90)))
        #transform = np.matmul(transform,roty(math.radians(-180)))
        cam.matrix_world = Matrix(transform)
        group.objects.link(cam)
        
cams_sphere(100)
I'm assuming that this is not a math.se question,  but a blender.se question, therefore [frame challenge] I'd suggest to use the builtin types and methods.

If you want to know why your math doesn't work, Blender.SE might not be the right place.

I will add some more tips to restructure your code, but take them with grain of salt, as I am no software dev.


I would also discourage the excessive scoping of types from imports at the start of functions. I can see at a glance that np.sin is a numpy function, but have a hard time otherwise.
The x, y, z tuple in the sphere_golden_spiral function was barely readable.   


  PEP: Limit all lines to a maximum of 79 characters

Separate the lookat function from a "position" functions. Right now the lookat function produces a matrix containing the position. This is not very intuitive.
Use enumerate if you want both index and object. (Not range(len(...)).)
If you want to multiply your numpy array times 10, don't multiply the individual elements, multiply them all at once.
Replace look_at with the built-in to_track_quat.
Use the built-in Vector, Quaternion and Matrix types.


A modified script.

import bpy
import numpy as np
import mathutils

def sphere_golden_spiral(num_pts=1000):
    indices = np.arange(0, num_pts, dtype=float) + 0.5

    phi = np.arccos(1 - 2*indices/num_pts)
    theta = np.pi * (1 + 5**0.5) * indices

    x = np.cos(theta) * np.sin(phi)
    y = np.sin(theta) * np.sin(phi)
    z = np.cos(phi)
    points = np.stack((x,y,z),axis=-1)
    return points

def cams_sphere(num_pts):
    points = sphere_golden_spiral(num_pts) * 10

    for i, point in enumerate(points):
        bpy.ops.object.camera_add()
        cam = bpy.context.object
        cam.name='Traj_sphere_%i'%i

        cam.location = mathutils.Vector(point)
        cam.rotation_mode = 'QUATERNION'
        cam.rotation_quaternion = cam.location.to_track_quat('Z', 'Y')

cams_sphere(100)


Obviously, you can use the longer (possibly more stable) version with a matrix multiplication.

        bpy.ops.object.camera_add()
        cam = bpy.context.object
        cam.name='Traj_sphere_%i'%i

        location = mathutils.Vector(point)
        rotation = location.to_track_quat('Z', 'Y')

        mat_loc = mathutils.Matrix.Translation(location)
        mat_rot = rotation.to_matrix().to_4x4()

        cam.matrix_world = mat_loc * mat_rot