import { BoundingRectangle } from "../../Source/Cesium.js"; import { Color } from "../../Source/Cesium.js"; import { ComponentDatatype } from "../../Source/Cesium.js"; import { IndexDatatype } from "../../Source/Cesium.js"; import { PrimitiveType } from "../../Source/Cesium.js"; import { WebGLConstants } from "../../Source/Cesium.js"; import { WindingOrder } from "../../Source/Cesium.js"; import { Buffer } from "../../Source/Cesium.js"; import { BufferUsage } from "../../Source/Cesium.js"; import { ClearCommand } from "../../Source/Cesium.js"; import { ContextLimits } from "../../Source/Cesium.js"; import { DrawCommand } from "../../Source/Cesium.js"; import { RenderState } from "../../Source/Cesium.js"; import { ShaderProgram } from "../../Source/Cesium.js"; import { VertexArray } from "../../Source/Cesium.js"; import { BlendingState } from "../../Source/Cesium.js"; import createContext from "../createContext.js"; describe( "Renderer/Draw", function () { var context; var sp; var va; beforeAll(function () { context = createContext(); }); afterAll(function () { context.destroyForSpecs(); }); afterEach(function () { sp = sp && sp.destroy(); va = va && va.destroy(); }); it("draws a white point", function () { var fs = "void main() { gl_FragColor = vec4(1.0); }"; expect({ context: context, fragmentShader: fs, }).contextToRender(); }); it("draws a white point with an index buffer", function () { // Use separate context to work around IE 11.0.9 bug var context = createContext(); var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); // Two indices instead of one is a workaround for NVIDIA: // http://www.khronos.org/message_boards/viewtopic.php?f=44&t=3719 var indexBuffer = Buffer.createIndexBuffer({ context: context, typedArray: new Uint16Array([0, 0]), usage: BufferUsage.STATIC_DRAW, indexDatatype: IndexDatatype.UNSIGNED_SHORT, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], indexBuffer: indexBuffer, }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); sp = sp.destroy(); va = va.destroy(); context.destroyForSpecs(); }); it("draws a red point with two vertex buffers", function () { var vs = "attribute vec4 position;" + "attribute mediump float intensity;" + "varying mediump float fs_intensity;" + "void main() {" + " gl_PointSize = 1.0; " + " gl_Position = position;" + " fs_intensity = intensity;" + "}"; var fs = "varying mediump float fs_intensity; void main() { gl_FragColor = vec4(fs_intensity, 0.0, 0.0, 1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, intensity: 1, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, { index: 1, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 1, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, }); command.execute(context); expect(context).toReadPixels([255, 0, 0, 255]); }); it("draws a red point with one interleaved vertex buffers", function () { var vs = "attribute vec4 position;" + "attribute mediump float intensity;" + "varying mediump float fs_intensity;" + "void main() {" + " gl_PointSize = 1.0; " + " gl_Position = position;" + " fs_intensity = intensity;" + "}"; var fs = "varying mediump float fs_intensity; void main() { gl_FragColor = vec4(fs_intensity, 0.0, 0.0, 1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, intensity: 1, }, }); var stride = 5 * Float32Array.BYTES_PER_ELEMENT; var vertexBuffer = Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1, 1]), usage: BufferUsage.STATIC_DRAW, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: vertexBuffer, componentsPerAttribute: 4, offsetInBytes: 0, strideInBytes: stride, }, { index: 1, vertexBuffer: vertexBuffer, componentsPerAttribute: 1, offsetInBytes: 4 * Float32Array.BYTES_PER_ELEMENT, strideInBytes: stride, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, }); command.execute(context); expect(context).toReadPixels([255, 0, 0, 255]); }); it("draws with scissor test", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); // 1 of 3: Clear to black ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 3: Render point - fails scissor test var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ scissorTest: { enabled: true, rectangle: new BoundingRectangle(1, 1, 0, 0), }, }), }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 3: Render point - passes scissor test command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ scissorTest: { enabled: true, rectangle: new BoundingRectangle(0, 0, 1, 1), }, }), }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it("draws with color mask", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); // 1 of 3: Clear to black ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 3: Render point - blue color mask var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ colorMask: { red: true, green: false, blue: false, alpha: false, }, }), }); command.execute(context); expect(context).toReadPixels([255, 0, 0, 255]); // 3 of 3: Render point - red color mask (blue channel not touched) command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ colorMask: { red: false, green: false, blue: true, alpha: false, }, }), }); command.execute(context); expect(context).toReadPixels([255, 0, 255, 255]); }); it("draws with additive blending", function () { var cxt = createContext({ webgl: { alpha: true, }, }); var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(0.5); }"; var sp = ShaderProgram.fromCache({ context: cxt, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); var va = new VertexArray({ context: cxt, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: cxt, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); // 1 of 3: Clear to black ClearCommand.ALL.execute(cxt); expect(cxt).toReadPixels([0, 0, 0, 0]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ blending: { enabled: true, equationRgb: WebGLConstants.FUNC_ADD, // Optional, default equationAlpha: WebGLConstants.FUNC_ADD, // Optional, default functionSourceRgb: WebGLConstants.ONE, // Optional, default functionSourceAlpha: WebGLConstants.ONE, // Optional, default functionDestinationRgb: WebGLConstants.ONE, functionDestinationAlpha: WebGLConstants.ONE, }, }), }); // 2 of 3: Blend: 0 + 0.5 command.execute(cxt); expect({ context: cxt, epsilon: 1, }).toReadPixels([127, 127, 127, 127]); // 3 of 3: Blend: 0.5 + 0.5 command.execute(cxt); expect({ context: cxt, epsilon: 1, }).toReadPixels([254, 254, 254, 254]); sp.destroy(); va.destroy(); cxt.destroyForSpecs(); }); it("draws with alpha blending", function () { var cxt = createContext({ webgl: { alpha: true, }, }); var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 0.5); }"; var sp = ShaderProgram.fromCache({ context: cxt, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); var va = new VertexArray({ context: cxt, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: cxt, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); // 1 of 3: Clear to black ClearCommand.ALL.execute(cxt); expect(cxt).toReadPixels([0, 0, 0, 0]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ blending: { enabled: true, equationRgb: WebGLConstants.FUNC_ADD, equationAlpha: WebGLConstants.FUNC_SUBTRACT, // does not actually matter functionSourceRgb: WebGLConstants.SRC_ALPHA, functionSourceAlpha: WebGLConstants.ONE, // Don't blend alpha functionDestinationRgb: WebGLConstants.ONE_MINUS_SRC_ALPHA, functionDestinationAlpha: WebGLConstants.ZERO, }, }), }); // 2 of 3: Blend: RGB: (255 * 0.5) + (0 * 0.5), Alpha: 0.5 + 0 command.execute(cxt); expect({ context: cxt, epsilon: 1, }).toReadPixels([127, 127, 127, 127]); // 3 of 3: Blend: RGB: (255 * 0.5) + (127 * 0.5), Alpha: 0.5 + 0 command.execute(cxt); expect({ context: cxt, epsilon: 2, }).toReadPixels([191, 191, 191, 127]); sp.destroy(); va.destroy(); cxt.destroyForSpecs(); }); it("draws with blend color", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ blending: { enabled: true, color: { red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5, }, equationRgb: WebGLConstants.FUNC_SUBTRACT, equationAlpha: WebGLConstants.FUNC_SUBTRACT, functionSourceRgb: WebGLConstants.CONSTANT_COLOR, functionSourceAlpha: WebGLConstants.ONE, functionDestinationRgb: WebGLConstants.ZERO, functionDestinationAlpha: WebGLConstants.ZERO, }, }), }); // 2 of 3: Blend: RGB: 255 - 127, Alpha: 255 - (255 - 255) // Epsilon of 1 because ANGLE gives 127 and desktop GL gives 128. command.execute(context); expect({ context: context, epsilon: 1, }).toReadPixels([128, 128, 128, 255]); }); it("draws with culling", function () { var vs = "attribute vec4 position; void main() { gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([ -1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1, ]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); // 1 of 3: Clear to black ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 3: Cull front faces - nothing is drawn var command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ cull: { enabled: true, face: WebGLConstants.FRONT, }, }), }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 3: Cull back faces - nothing is culled command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ cull: { enabled: true, face: WebGLConstants.BACK, }, }), }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it("draws with front face winding order", function () { var vs = "attribute vec4 position; void main() { gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([ -1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1, ]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); // 1 of 3: Clear to black ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 3: Cull back faces with opposite winding order - nothing is drawn var command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ frontFace: WindingOrder.CLOCKWISE, cull: { enabled: true, face: WebGLConstants.BACK, }, }), }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 3: Cull back faces with correct winding order - nothing is culled command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ frontFace: WindingOrder.COUNTER_CLOCKWISE, cull: { enabled: true, face: WebGLConstants.BACK, }, }), }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it("draws with the depth test", function () { var vs = "attribute vec4 position; void main() { gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([ -1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1, ]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); var command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ depthTest: { enabled: true, func: WebGLConstants.LEQUAL, }, }), }); // 1 of 2. Triangle fan passes the depth test. var clearCommand = new ClearCommand({ color: new Color(0.0, 0.0, 0.0, 0.0), depth: 1.0, }); clearCommand.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); // 2 of 2. Triangle fan fails the depth test. clearCommand.color = new Color(0.0, 0.0, 0.0, 0.0); clearCommand.depth = 0.0; clearCommand.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); }); it("draws with depth range", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(gl_DepthRange.near, gl_DepthRange.far, 0.0, 1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ depthRange: { near: 0.25, far: 0.75, }, }), }); command.execute(context); expect(context).toReadPixels([64, 191, 0, 255]); }); it("draws with line width", function () { var vs = "attribute vec4 position; void main() { gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([ -1000, -1000, 0, 1, 1000, 1000, 0, 1, ]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.LINES, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ lineWidth: ContextLimits.maximumAliasedLineWidth, // May only be 1. }), }); command.execute(context); // I believe different GL implementations are allowed to AA // in different ways (or at least that is what we see in practice), // so verify it at least rendered something. expect(context).notToReadPixels([0, 0, 0, 255]); }); it("draws with polygon offset", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ polygonOffset: { enabled: true, factor: 1, units: 1, }, }), }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it("draws with sample coverage", function () { if (!context.antialias) { // Sample coverage requires antialiasing. return; } var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ sampleCoverage: { enabled: true, value: 0, invert: false, }, }), }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ sampleCoverage: { enabled: false, }, }), }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it("draws with stencil test (front)", function () { if (context.stencilBits === 0) { return; } var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([ -1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1, ]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); var rs = RenderState.fromCache({ stencilTest: { enabled: true, frontFunction: WebGLConstants.EQUAL, reference: 1, mask: 1, }, }); // 1 of 4. Clear, including stencil ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 4. Render where stencil is set - nothing is drawn var command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: rs, }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 4. Render to stencil only, increment command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ colorMask: { red: false, green: false, blue: false, alpha: false, }, stencilTest: { enabled: true, frontOperation: { zPass: WebGLConstants.INCR, }, }, }), }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 4 of 4. Render where stencil is set command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: rs, }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it("draws with stencil test (back)", function () { if (context.stencilBits === 0) { return; } var vs = "attribute vec4 position; void main() { gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([ -1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1, ]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); var rs = RenderState.fromCache({ frontFace: WindingOrder.CLOCKWISE, stencilTest: { enabled: true, backFunction: WebGLConstants.NOTEQUAL, reference: 0, }, }); // 1 of 4. Clear, including stencil ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 4. Render where stencil is set - nothing is drawn var command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: rs, }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 4. Render to stencil only, increment command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: RenderState.fromCache({ frontFace: WindingOrder.CLOCKWISE, colorMask: { red: false, green: false, blue: false, alpha: false, }, stencilTest: { enabled: true, backOperation: { zPass: WebGLConstants.INVERT, }, }, }), }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 4 of 4. Render where stencil is set command = new DrawCommand({ primitiveType: PrimitiveType.TRIANGLE_STRIP, shaderProgram: sp, vertexArray: va, renderState: rs, }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it("draws with an offset and count", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, -1, 0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // The first point in the vertex buffer does not generate any pixels var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, offset: 0, count: 1, shaderProgram: sp, vertexArray: va, }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, offset: 1, count: 1, shaderProgram: sp, vertexArray: va, }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it("draws two instances of a point with different per-instance colors", function () { if (context.instancedArrays) { var vs = "attribute vec4 position;" + "attribute vec4 color;" + "varying vec4 v_color;" + "void main() {" + " gl_PointSize = 1.0; " + " gl_Position = position;" + " v_color = color;" + "}"; var fs = "varying vec4 v_color; void main() { gl_FragColor = v_color; }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, color: 1, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, { index: 1, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Uint8Array([255, 0, 0, 255, 0, 255, 0, 255]), usage: BufferUsage.STATIC_DRAW, }), componentDatatype: ComponentDatatype.UNSIGNED_BYTE, componentsPerAttribute: 4, normalize: true, instanceDivisor: 1, }, ], }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, instanceCount: 2, renderState: RenderState.fromCache({ blending: BlendingState.ADDITIVE_BLEND, }), }); command.execute(context); expect(context).toReadPixels([255, 255, 0, 255]); } }); it("fails to draw (missing command)", function () { expect(function () { context.draw(); }).toThrowDeveloperError(); }); it("fails to draw (missing shaderProgram)", function () { expect(function () { context.draw({ primitiveType: PrimitiveType.POINTS, }); }).toThrowDeveloperError(); }); it("fails to draw (missing primitiveType)", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, }); expect(function () { context.draw({ shaderProgram: sp, }); }).toThrowDeveloperError(); }); it("fails to draw (primitiveType)", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, }); expect(function () { context.draw({ primitiveType: "invalid value", shaderProgram: sp, }); }).toThrowDeveloperError(); }); it("fails to draw (missing vertexArray)", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, }); expect(function () { context.draw({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, }); }).toThrowDeveloperError(); }); it("fails to draw (negative offset)", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, }); expect(function () { context.draw({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: new VertexArray({ context: context, }), offset: -1, count: 1, }); }).toThrowDeveloperError(); }); it("throws if instanceCount is less than one", function () { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, instanceCount: -1, }); expect(function () { command.execute(context); }).toThrowDeveloperError(); }); it("throws when instanceCount is greater than one and the instanced arrays extension is not supported", function () { if (!context.instancedArrays) { var vs = "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; var fs = "void main() { gl_FragColor = vec4(1.0); }"; sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: { position: 0, }, }); va = new VertexArray({ context: context, attributes: [ { index: 0, vertexBuffer: Buffer.createVertexBuffer({ context: context, typedArray: new Float32Array([0, 0, 0, 1]), usage: BufferUsage.STATIC_DRAW, }), componentsPerAttribute: 4, }, ], }); var command = new DrawCommand({ primitiveType: PrimitiveType.POINTS, shaderProgram: sp, vertexArray: va, instanceCount: 2, }); expect(function () { command.execute(context); }).toThrowDeveloperError(); } }); }, "WebGL" );