Rendering 3D images with Blender and NextJs

Hello guys,

I am planning to use docker for the first time in a project. My plan is to use two containers. The first one includes a NextJs app that can be accessed through the browser. The second one should use blender anyhow.

On demand I want to send a request to the blender container to render a specific scene. Blender does not need to be accessible from the host, but only the NextJs app should be able to access Blender.

It is therefore sufficient that Blender runs headless - without GUI. I would like to render the scene using a python script - like I do with Blender CLI on Windows: “blender.exe --background --python script.py”. The rendered image should then be sent back to the NextJs app so I can download it in the browser.

I came across two images on Docker Hub: nytimes/blender and linuxserver/blender but I am not sure if they are helpful here.

How am I able to accomplish that? I would really appreciate if you can guide me how to implement this.

Thank you!

Best

… I tested some methods. My current approach is to use two containers as mentioned above.

First container - NextJs

FROM node:16
WORKDIR /app
COPY package*.json .
RUN npm i
COPY . .
EXPOSE 4000
CMD ["npm", "run", "dev"]

Second container - node & blender

FROM nytimes/blender:3.0-cpu-ubuntu18.04
RUN apt-get update \
  && apt-get install -y curl gnupg build-essential \
  && curl --silent --location https://deb.nodesource.com/setup_16.x | bash - \
  && apt-get remove -y --purge cmdtest \
  && apt-get update \
  && apt-get install -y nodejs
WORKDIR /app
COPY package*.json .
RUN npm i
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

As you can see I used the blender image from nytimes. I have no clue what’s an appropriate way to get access to blender from the nextjs app so I added an nodejs express server to the blender container. So I am able to call blender via api.

App.js

const express = require("express")
const app = express()
const router = express.Router()
const path = require("path")

function execShellCommand(cmd) {
  const exec = require('child_process').exec
    return new Promise((resolve, reject) => {
    exec(cmd, (error, stdout, stderr) => {
      if (error) console.warn(error)
      resolve(stdout ? stdout : stderr)
    })
  })
}

router.get("/", async (req, res) => {
  try {
    const blendFile = path.join(__dirname, '/test.blend')
    const outputPath = path.join(__dirname, '/test.png')
    const rsp = await execShellCommand(`blender -b ${blendFile} -o ${outputPath} -f 1`)
    res.send(rsp)
  } catch (err) {
    res.send(err)
  }
})

app.use("/", router)

const server = app.listen(process.env.port || 3000, () => {
  console.log(`Server listening on ${server.address().address}:${server.address().port}`)
})

This works but I am pretty sure that this is not best practice. I would love to get some hints from you.

Best