Skip to content

Instantly share code, notes, and snippets.

@valerysntx
Forked from bsergean/README.md
Created December 23, 2017 05:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save valerysntx/6668fd940770121f566cb76b14de8921 to your computer and use it in GitHub Desktop.
Save valerysntx/6668fd940770121f566cb76b14de8921 to your computer and use it in GitHub Desktop.
offscreen rendering with three.js and headless-gl, in coffee-script

Getting the code

git clone https://gist.github.com/6780d7cc0cabb1b4d6c8.git

Executing the code

$ npm install # maybe npm start will take care of it but just in case
$ npm start && open out.png

> offscreen-sample@1.0.0 start /Users/bsergean/src/offscreen_sample
> coffee offscreen_sample.coffee

THREE.WebGLRenderer 71
THREE.WebGLRenderer: TypeError: Object #<Object> has no method 'addEventListener'
THREE.WebGLRenderer: OES_texture_float extension not supported.
THREE.WebGLRenderer: OES_texture_float_linear extension not supported.
THREE.WebGLRenderer: OES_texture_half_float extension not supported.
THREE.WebGLRenderer: OES_texture_half_float_linear extension not supported.
THREE.WebGLRenderer: OES_standard_derivatives extension not supported.
THREE.WebGLRenderer: OES_element_index_uint extension not supported.
THREE.WebGLRenderer: EXT_texture_filter_anisotropic extension not supported.
Image written: out.png

Those warnings are harmless for our test case, but might be problematic for some folks. Support for extension is planned and coming -> stackgl/headless-gl#5

If you are on Linux you will need Xvfb. One way to do it:

$ xvfb-run -s "-ac -screen 0 1280x1024x24” node_modules/.bin/coffee cmd_antialias.coffee -i test_aliased.png -o out.png

More infos here -> https://github.com/stackgl/headless-gl#how-can-headless-gl-be-used-on-a-headless-linux-machine

Inspecting the output

Tada ! You just created an image thanks to OpenGL and many awesome libraries. How cool is that. Now open the output image. On a Mac you can just do that:

open out.png

I can't figure out how to add a .png to a gist. I've updaloaded the very non-impressive image here -> http://imgur.com/Vq4FnN9

Bummer, the sample is in coffee-script

npm run compile

This will compile the .coffee file to javascript and print it in your terminal. It's almost the same as the .coffee.

# The required node modules
THREE = require('three')
PNG = require('pngjs').PNG
gl = require("gl")()
fs = require('fs')
# Parameters (the missing one is the camera position, see below)
width = 600
height = 400
path = 'out.png'
png = new PNG({ width: width, height: height })
# THREE.js business starts here
scene = new THREE.Scene()
# camera attributes
VIEW_ANGLE = 45
ASPECT = width / height
NEAR = 0.1
FAR = 100
# set up camera
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR)
scene.add(camera)
camera.position.set(0, 2, 2)
camera.lookAt(scene.position)
# mock object, not used in our test case, might be problematic for some workflow
canvas = new Object()
# The width / height we set here doesn't matter
renderer = new THREE.WebGLRenderer({
antialias: true,
width: 0,
height: 0,
canvas: canvas, # This parameter is usually not specified
context: gl # Use the headless-gl context for drawing offscreen
})
# add some geometry
geometry = new THREE.BoxGeometry( 1, 1, 1 )
# add a material; it has to be a ShaderMaterial with custom shaders for now
# this is a work in progress, some related link / issues / discussions
#
# https://github.com/stackgl/headless-gl/issues/26
# https://github.com/mrdoob/three.js/pull/7136
# https://github.com/mrdoob/three.js/issues/7085
material = new THREE.ShaderMaterial()
vec4 = new THREE.Vector4( 1.0, 0.0, 0.0, 1.0 ) # red
material.vertexShader = '''
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
'''
material.fragmentShader = '''
uniform vec4 solidColor;
void main() {
gl_FragColor = solidColor;
}
'''
material.uniforms = { solidColor: { type: "v4", value: vec4 } }
# Create the mesh and add it to the scene
cube = new THREE.Mesh(geometry, material)
scene.add(cube)
# Let's create a render target object where we'll be rendering
rtTexture = new THREE.WebGLRenderTarget(
width, height, {
minFilter: THREE.LinearFilter,
magFilter: THREE.NearestFilter,
format: THREE.RGBAFormat
})
# render
renderer.render(scene, camera, rtTexture, true)
# read render texture into buffer
gl = renderer.getContext()
# create a pixel buffer of the correct size
pixels = new Uint8Array(4 * width * height)
# read back in the pixel buffer
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
# lines are vertically flipped in the FBO / need to unflip them
for j in [0...height]
for i in [0...width]
k = j * width + i
r = pixels[4*k]
g = pixels[4*k + 1]
b = pixels[4*k + 2]
a = pixels[4*k + 3]
m = (height - j + 1) * width + i
png.data[4*m] = r
png.data[4*m + 1] = g
png.data[4*m + 2] = b
png.data[4*m + 3] = a
# Now write the png to disk
stream = fs.createWriteStream(path)
png.pack().pipe stream
stream.on 'close', () ->
# We're done !!
console.log("Image written: #{ path }")
{
"name": "offscreen-sample",
"version": "1.0.0",
"scripts": {
"start": "coffee offscreen_sample.coffee",
"compile": "coffee -c -b offscreen_sample.coffee && cat offscreen_sample.js"
},
"dependencies": {
"three": "latest",
"pngjs": "latest",
"gl": "latest"
},
"devDependencies": {
"coffee-script": "latest"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment