Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update & optimize #3

Merged
merged 9 commits into from
Apr 1, 2016
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

![img](http://i.imgur.com/bROGMVq.png)

A helper module for unit testing shaders and comparing the result of `gl_FragColor` from a 1x1 WebGL canvas. See [glsl-hsl2rgb](https://github.com/Jam3/glsl-hsl2rgb) for a practical example.
A helper module for unit testing shaders and comparing the result of `gl_FragColor` from a 1x1 WebGL canvas. See [glsl-hsl2rgb](https://github.com/Jam3/glsl-hsl2rgb) for a practical example.

Example:
Example:

```js
var ShaderOutput = require('gl-shader-output')
Expand Down Expand Up @@ -46,18 +46,21 @@ var almostEqual = require('array-almost-equal')
almostEqual(color2, [0.0, 0.5, 0.0, 1.0], epsilon)
```

You can use this with tools like [smokestack](https://github.com/hughsk/smokestack) for test-driven GLSL development.
You can use this with tools like [smokestack](https://github.com/hughsk/smokestack) for test-driven GLSL development.

## Usage

[![NPM](https://nodei.co/npm/gl-shader-output.png)](https://www.npmjs.com/package/gl-shader-output)

#### `draw = ShaderOutput(opt)`
#### `draw = ShaderOutput(shader?, options?)`

Takes the following options, and returns a `draw` function.
Takes fragment shader source with the following options, and returns a `draw` function. Possible options:

- `shader` the shader (required), can be a function that accepts `gl` or an instance of gl-shader
- `gl` the gl state to re-use, expected to hold a 1x1 canvas (creates a new one if not specified)
- `gl` the gl state to re-use, expected to hold a 1x1 canvas (creates a new one if not specified, or uses the context of the shader, if gl-shader passed as the first argument)
- `width` the width of gl context, by default `1`
- `height` the height of gl context, by default `1`
- `shader` the shader, can be a source code of fragment shader, a function that accepts `gl` or an instance of gl-shader. Same as passing shader as the first argument.
- `float` use float values format for processing, if possible.
- [webgl-context](https://www.npmjs.com/package/webgl-context) options such as `alpha` and `premultipliedAlpha`

The draw function has the following signature:
Expand Down
106 changes: 89 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,113 @@
var create = require('webgl-context')
var getPixels = require('canvas-pixels').get3d
var triangle = require('a-big-triangle')
var xtend = require('xtend')
var assign = require('xtend/mutable')
var glExt = require('webglew')
var Framebuffer = require('gl-fbo')
var Shader = require('gl-shader')

module.exports = function(opt) {
opt = xtend({
width: 1,

module.exports = function (shader, opt) {
if (!opt) {
//just options
if (typeof shader === 'object' && !shader.fragShader) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small thing here – I think it's best to reduce the peer dependency on fragShader and gl-shader and just assume a more generic interface that is basically { gl, bind() } which is what we had before. 😄 But I think this part will go away if we keep the options simple as they were before.

opt = shader
}
//just a shader
else {
opt = {
shader: shader
}
}
}
else {
opt.shader = shader
}

opt = xtend({
width: 1,
height: 1,
preserveDrawingBuffer: true
preserveDrawingBuffer: true,
float: true
}, opt)
var gl = opt.gl || create(opt)

var gl = opt.shader && opt.shader.gl || opt.gl || create(opt)
if (!opt.shader)
throw new Error('no shader supplied to gl-shader-output')


//set gl context dims
gl.canvas.width = opt.width
gl.canvas.height = opt.height

var shader = typeof opt.shader === 'function'
? opt.shader(gl)
: opt.shader

//create gl-shader, if only fragment shader source
if (typeof shader === 'string') {
shader = Shader(gl, '\
attribute vec2 position;\
void main() {\
gl_Position = vec4(position, 1.0, 1.0);\
}\
' , shader)
}

//micro optimizations
gl.disable(gl.DEPTH_TEST)
gl.disable(gl.BLEND)
gl.disable(gl.CULL_FACE)
gl.disable(gl.DITHER)
gl.disable(gl.POLYGON_OFFSET_FILL)
gl.disable(gl.SAMPLE_COVERAGE)
gl.disable(gl.SCISSOR_TEST)
gl.disable(gl.STENCIL_TEST)

var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 3, 3, -1]), gl.STATIC_DRAW)
shader.attributes.position.pointer()

//try to use floats
var float = glExt(gl).OES_texture_float && opt.float
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid some complexity of webglew here by just using gl.getExtension.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another note – maybe opt.float should be the first condition. gl.getExtension will actually enable the extension, no point in enabling it unless the user explicitly wants it.


//set framebuffer as a main target
var framebuffer = new Framebuffer(gl, [opt.width, opt.height], {
float: float,
depth: false,
color: 1
})
framebuffer.bind()
shader.bind()


function process(uniforms) {
var w = gl.drawingBufferWidth, h = gl.drawingBufferHeight

gl.clearColor(0, 0, 0, 0)
gl.clear(gl.COLOR_BUFFER_BIT)

shader.bind()

//if user specifies some uniforms
if (uniforms)
if (uniforms) {
shader.bind()
assign(shader.uniforms, uniforms)
}

//full-screen quad
triangle(gl)
gl.drawArrays(gl.TRIANGLES, 0, 3);

if (float) {
var pixels = new Float32Array(w * h * 4)
gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, pixels)
}
else {
var pixels = new Uint8Array(w * h * 4)
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
pixels = new Float32Array(pixels).map(function(p) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Float32Array#map doesn't work in Safari and probably some other older browsers. 😄

return p / 255
})
}

var pixels = Array.prototype.slice.call(getPixels(gl))
return pixels.map(function(p) {
return p / 255
})
return pixels
}
return process
}
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
"url": "https://github.com/mattdesl"
},
"dependencies": {
"a-big-triangle": "^1.0.0",
"canvas-pixels": "0.0.0",
"gl-fbo": "^2.0.5",
"webgl-context": "^2.1.1",
"webglew": "^1.0.5",
"gl-shader": "^4.2.0",
"xtend": "^4.0.0"
},
"devDependencies": {
"faucet": "0.0.1",
"glslify": "^1.6.0",
"glslify": "^5.0.2",
"smokestack": "^3.2.0",
"tap-closer": "^1.0.0",
"tape": "^3.4.0",
Expand Down
46 changes: 25 additions & 21 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,54 @@ var test = require('tape')
var create = require('../')
var glslify = require('glslify')
var FuzzyArray = require('test-fuzzy-array')
var Shader = require('gl-shader');
var createGl = require('webgl-context');

test('should return the color blue', function(t) {
var shader = glslify({
vertex: './shaders/test.vert',
fragment: './shaders/blue.frag'
})

var draw = create({
shader: shader
shader: function (gl) {
return Shader(gl,
glslify('./shaders/test.vert'),
glslify('./shaders/blue.frag')
)
},
float: false
})

t.deepEqual(draw(), [0, 0, 1, 1])
t.end()
})

test('should be able to handle alpha', function(t) {
var shader = glslify({
vertex: './shaders/test.vert',
fragment: './shaders/alpha.frag'
})

var draw = create({
shader: shader
})
var draw = create(glslify('./shaders/alpha.frag'))

t.deepEqual(draw(), [0, 0, 1, 0])
t.end()
})

test('should accept uniforms', function(t) {
var shader = glslify({
vertex: './shaders/test.vert',
fragment: './shaders/uniforms.frag'
})
var shader = Shader(createGl({width: 1, height: 1}),
glslify('./shaders/test.vert'),
glslify('./shaders/uniforms.frag')
)

var draw = create({
shader: shader
})

var input = [0, 0.25, 0.5, 1.0]
var reversed = input.slice().reverse()

var almost = FuzzyArray(t, 0.01)
almost(draw({ u_value: input, multiplier: 1.0 }), reversed)
almost(draw({ u_value: input, multiplier: 3.0 }), [ 1, 1, 0.75, 0 ])
almost(draw({ u_value: input, multiplier: 3.0 }), [ 3, 1.5, 0.75, 0 ])
t.end()
})

test('should process n-dim input', function (t) {
var draw = create(glslify('./shaders/blue.frag'), {
width: 2,
height: 2
});
t.deepEqual(draw(), [0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1])
t.end()
})