Today we are applying camera mapping to a texture. The new projection system includes a perspective projection, which can be useful if you want to model a projector (using the distribution pin of a light source). You can also use it for camera mapping but at the moment you have to manually match the transform with the current camera position. But of course you can also doing it through Lua.
To use this script, start rendering, select a material or image texture (you can use the material picker) and then run this script.
The first step is to get the current camera. This only works after you start rendering.
Code: Select all
-- current render target and camera
rendertarget = octane.render.getRenderTargetNode()
camera = rendertarget:getInputNode(octane.P_CAMERA)
Code: Select all
-- camera transform
pos = camera:getPinValue(octane.P_POSITION)
target = camera:getPinValue(octane.P_TARGET)
up = octane.vec.normalized(camera:getPinValue(octane.P_UP))
Z = octane.vec.normalized(octane.vec.sub(target, pos))
X = octane.vec.normalized(octane.vec.cross(up, Z))
Y = octane.vec.normalized(octane.vec.cross(Z, X))
matrix = {
{X[1], Y[1], Z[1], pos[1]},
{X[2], Y[2], Z[2], pos[2]},
{X[3], Y[3], Z[3], pos[3]},
}
We also construct the matrix for the UV transform. We need a (-0.5, -0.5) offset to center the image (by default the origin is on the projection axis, which is mapped to the middle of the image), plus compensation for the lens shift.
Code: Select all
-- field of view, lens shift and resolution
fov = camera:getPinValue(octane.P_FOV)
shift = camera:getPinValue(octane.P_LENS_SHIFT)
resolution = rendertarget:getPinValue(octane.P_RESOLUTION)
w = 2 * math.tan(math.rad(fov / 2))
h = w * resolution[2] / resolution[1]
matrix = octane.matrix.mul(matrix, octane.matrix.makeScale{-w, h, 1})
uvmatrix = octane.matrix.makeTranslation{-.5 + shift[1], -.5 + shift[2], 0}
P_PROJECTION
and P_TRANSFORM
inputs with new nodes, and fill in the matrices we just calculated.
Code: Select all
-- get selected texture or material node, and apply transform
function project(tex)
proj = octane.node.create{
type = octane.NT_PROJ_PERSPECTIVE,
pinOwnerNode = tex,
pinOwnerId = octane.P_PROJECTION,
}
octane.node.create{
type = octane.NT_TRANSFORM_VALUE,
pinOwnerNode = tex,
pinOwnerId = octane.P_TRANSFORM,
}
tex:setPinValue(octane.P_TRANSFORM, uvmatrix)
proj:setPinValue(octane.P_TRANSFORM, matrix)
end
P_PROJECTION
and P_TRANSFORM
pins, which probably means the given node is an image texture or procedural texture.Code: Select all
-- octane.node.getPinInfo raises an error when the pin doesn't exist.
function hasPin(n, pin)
local r = pcall(n.getPinInfo, n, pin)
return r
end
function hasProjection(n)
return hasPin(n, octane.P_PROJECTION) and hasPin(n, octane.P_TRANSFORM)
end
octane.node.getInputNodeIx
raises an error when the pin isn't connected, so we wrap this call with pcall()
so this error doesn't terminate our script.
Code: Select all
foundtex = false
for _, n in ipairs(octane.project.getSelection()) do
if hasProjection(n) then
foundtex = true
project(n)
elseif n:getProperties().outputType == octane.PT_MATERIAL then
for i = 1, n:getPinCount() do
m = nil
pcall(function() m = n:getInputNodeIx(i) end)
if m ~= nil and hasProjection(m) then
foundtex = true
project(m)
end
end
end
end
octane.changemanager.update()
to propagate the changes to the renderer. If we didn't find anything we inform the caller of the problem.
Code: Select all
if foundtex then
octane.changemanager.update()
else
error("You have to select an image texture node or material node.")
end
--
Roeland