Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A low-level ES6 WebGL rendering framework

License

NotificationsYou must be signed in to change notification settings

kbirk/esper

Repository files navigation

npm versionBower versionBuild StatusCoverage StatusDependencies Status

A low-level ES6 WebGL rendering framework.

Table of Contents

Installation

Requiresnode orbower.

npm install esper

or

bower install esper

Documentation

Example

letshader;letviewport;letrenderable;lettexture;functionrender(){// setupviewport.push();shader.use();shader.setUniform('uLightPosition',light);shader.setUniform('uModelMatrix',model);shader.setUniform('uViewMatrix',view);shader.setUniform('uProjectionMatrix',projection);shader.setUniform('uTextureSampler',0);texture.bind(0);// drawrenderable.draw();// teardowntexture.unbind();viewport.pop();// queue next framerequestAnimationFrame(render);}// create webgl contexttry{gl=esper.WebGLContext.get("glcanvas");}catch(err){console.err(err.message);}// only continue if WebGL is availableif(gl){// viewportviewport=newesper.Viewport({width:window.innerWidth,height:window.innerHeight});// shadershader=newesper.Shader({vert:phong.vert,frag:phong.frag});// texturetexture=newesper.ColorTexture2D({src:[255,0,0,255,0,255,0,255,0,0,255,255,255,255,0,255],width:2,height:2,wrap:'CLAMP_TO_EDGE',filter:'NEAREST'})// renderablerenderable=newesper.Renderable({vertices:{0:cube.positions,1:cube.normals,2:cube.uvs},indices:cube.indices});// enable depth testinggl.enable(gl.DEPTH_TEST);// start render looprender();}

Usage

WebGLContexts

In order to access the WebGL API you first need a canvas element from which a WebGL rendering context can be created. Theesper.WebGLContext namespace streamlines the typical process of instantiating a context and provides facilities for handling multiple contexts within a single application. The object returned is a native WebGLRenderingContext object.

// Get WebGL context and load all available extensions.letgl;try{gl=esper.WebGLContext.get('canvas-id');}catch(err){console.error(err.message);}

Options can be provided via the second parameter.

letgl;try{gl=esper.WebGLContext.get(canvasDOMElement,{antialias:falsedepth:false});}catch(err){console.error(err.message);}

Once a context has been created, it is bound internally and can be accessed throughout the application by callingesper.WebGLContext.get. It is important to note that allesper classes will use the context bound during their instantiation. This is only important if you are intending to use multiple WebGL contexts. In most cases this is discouraged as WebGL constructs cannot be shared between contexts and result in redundant buffers and textures.

letgl=esper.WebGLContext.get();

Theesper.WebGLContext.bind method can be used to manually bind a context. Once again, if only one context is ever used, this is unnecessary as the context is bound upon creation.

esper.WebGLContext.bind('canvas-id');esper.WebGLContext.bind(canvasDOMElement);

During the creation of the context, esper will automatically attempt to load all known WebGL extensions. To check whether or not a specific extension has been successfully loaded useesper.WebGLContext.checkExtension.

// Check if the bound WebGL context supports depth textures.if(esper.WebGLContext.checkExtension('WEBGL_depth_texture')){console.log('Depth textures are supported');}

All supported or unsupported extensions can also be queried.

esper.WebGLContext.supportedExtensions().forEach(extension=>{console.log(extensions+' is supported.');});esper.WebGLContext.unsupportedExtensions().forEach(extension=>{console.log(extensions+' is not supported.');});

Contexts can be removed via theesper.WebGLContext.remove method.

esper.WebGLContext.remove('canvas-id');

Shaders

Shaders are programs that execute on the GPU and are essential to 3D programming. WebGL currently supports two types of shaders: vertex and fragment. Vertex shaders execute on each vertex of the primitive being rendered while fragment shaders execute for each rasterized fragment. Theesper.Shader constructor accepts both URLs to source files and source code as strings.

// Create shader object and using source URLs (also supports source code strings).letshader=newesper.Shader({vert:'shaders/phong.vert',frag:'shaders/phong.frag'},(err,shader)=>{if(err){console.error(err);return;}// Shader completion callback.console.log('Shader sources loaded and program instantiated');});

Multiple source arguments can be provided as arrays and are concatenated together in the respective order. Common code can also be shared between shader types and is appended at the top of the source.

// Create shader object and using source URLs (also supports source code strings).letshader=newesper.Shader({common:'uniform highp float uTime;',vert:`shaders/noise.vertattribute highp vec3 aVertexPosition;void main() {gl_Position = vec4(aVertexPosition * noise(uTime), 1.0);vPosition = aVertexPosition.xyz;}`,frag:`void main() {gl_FragColor = vec4(1*uTime, 1*uTime, 1*uTime, 1.0);}`},(err,shader)=>{if(err){console.error(err);return;}console.log('Shader sources loaded and program instantiated');});

During shader initialization the shader source code is parsed and analyzed. All vertex attribute locations are bound in the order in which they are declared and all uniform types and locations are retrieved and stored based upon name. This is used to greatly simplify the process of uploading uniforms to the GPU.

When uploading uniforms to the GPU, arguments are automatically casted (within reason) into the appropriate format.

// Upload uniforms to the GPUshader.setUniform('uProjectionMatrix',projectionMatrixArray);shader.setUniform('uAlpha',0.25);shader.setUniform('uHasTexture',false);// booleans are converted to float

Viewports

Anesper.Viewport defines a rendering resolution within the canvas element. By default, the viewport will be set to the current dimensions of the canvas element.

// Create the viewport.letviewport=newesper.Viewport({width:window.innerWidth,height:window.innerHeight});// Have the viewport always fit to the window.window.addEventListener('resize',()=>{viewport.resize(window.innerWidth,window.innerHeight);});// Push the viewport onto the stack at its current size.viewport.push();// Push viewport overrides onto the stack, this will give a 10px border.viewport.push(10,10,canvas.height-20,canvas.width-20);// Pop the override off the stack.viewport.pop();

Using theresize method will resize the underlying canvas element along with the implicit dimensions of the viewport.

// Resize the viewport. This resizes the underlying canvas.viewport.resize(460,460);

Modifying the viewport is the recommended way to mimic multiple rendering contexts as it requires no duplication of WebGL constructs.

letviewport=newesper.Viewport({width:1000,height:500});viewport.push(0,0,500,500);// ... render to left half of canvasviewport.pop();viewport.push(500,0,500,500);// .. render to right half of canvasviewport.pop();

VertexBuffers

Anesper.VertexBuffer is used to store vertex attribute information. Common attributes include positions, normals, texture coordinates, skeletal animation joint ids and skinning weights. Attributes can be stored in separate isolated buffers or together in a single buffer either accessed at byte offsets or interleaved with each other to take advantage of cache locality (recommended).

// Create separate vertex buffers for each attributes.letpositionBuffer=newesper.VertexBuffer(positions,{0:{size:3,type:'FLOAT'}});letnormalBuffer=newesper.VertexBuffer(normals,{1:{size:3,type:'FLOAT'}});letuvBuffer=newesper.VertexBuffer(uvs,{2:{size:2,type:'FLOAT'}});// Create interleaved buffer from an existing Array or Float32Array.letvertexBuffer=newesper.VertexBuffer(array,{0:{size:3,type:'FLOAT',byteOffset:0},1:{size:3,type:'FLOAT',byteOffset:12},2:{size:2,type:'FLOAT',byteOffset:26}});

Drawing withesper.VertexBuffers is easy, simply bind it, and draw.

// Bind vertex buffer.vertexBuffer.bind();// Draw triangles.vertexBuffer.draw({mode:TRIANGLES});// Draw points from an index offset.vertexBuffer.draw({mode:POINTS,indexOffset:100});// Draw n lines.vertexBuffer.draw({mode:LINES,count:n*2});// UnbindvertexBuffer.unbind();

VertexPackages

Interleaving vertex attributes and manually defining the attribute pointers is tedious and prone to frustrating user error. Theesper.VertexPackage class simplifies this and coalesces multiple arrays into a single interleaved Float32Array while calculating the correct attribute pointers.

// Create interleaved vertex buffers using vertex packages.letvertexPackage=newesper.VertexPackage({0:positions,1:normals,2:uvs});

An instantiatedesper.VertexPackage can then be passed to aesper.VertexBuffer constructor for simple instantiation.

letvertexBuffer=newesper.VertexBuffer(vertexPackage.buffer,vertexPackage.pointers);

IndexBuffers

Due to the nature of tessellation, single vertices may referenced by multiple geometric primitives. Solely using vertex buffers can result in a large amount of redundancy as these vertices may be repeated within the buffer. For example a simple cube is composed of 12 triangles, requiring 36 vertices if only using a vertex buffer. With flat shading this cube be represented with only 8 vertices when using an index buffer. Theesper.IndexBuffer class allows the user to specify the ordering of vertex attributes inside a vertex buffer, which allows re-use of vertices and smaller, more efficient buffers.

// Create index buffer from an array of indices.letindexBuffer=newesper.IndexBuffer(indices);

Rendering using anesper.IndexBuffer is easy as well, simply bind any referenced vertex buffers, then bind the index buffer and draw.

// Bind vertex buffer.vertexBuffer.bind();// Draw triangles.indexBuffer.draw({mode:TRIANGLES});// Draw n lines.indexBuffer.draw({mode:LINES,count:n*2});// Draw points from an offset.indexBuffer.draw({mode:POINTS,byteOffset:100*(4*3),count:n-(100)});vertexBuffer.unbind();

Renderables

While working at the level ofesper.VertexBuffer andesper.IndexBuffer can give you low level control, it is often tedious and unnecessary. Theesper.Renderable class encapsulates this behavior with a simplified interface while still retaining the same low level control.

// Create a renderable from vertex and index arrays.letrenderable=newesper.Renderable({vertices:{0:positions,1:normals,2:uvs},indices:indices});// Create a renderable without an index bufferletrenderable=newesper.Renderable({vertices:{0:positions,1:normals,2:uvs}});// Draw the renderable.renderable.draw({mode:'LINES',// render lines instead of trianglesbyteOffset:100*(4*3)*2,// exclude the first 100 linescount:500*2// only render 500 lines});

Textures

Textures can be used to store and sample many different types of information. Typically a texture stores color values but can also be used to store depth values or any other types of encoded information. Anesper.Texture2D is a low level construct for interfacing directly with the WebGL texture API.

// Create texture from image URL.lettexture=newesper.Texture2D({src:newUint8Array([255,0,0,255,0,255,0,255,0,0,255,255,0,255,0,255,]),width:2,height:2,format:'RGBA',type:'UNSIGNED_BYTE',wrapS:'REPEAT',wrapT:'REPEAT',minFlter:'LINEAR',magFilter:'LINEAR',invertY:false,premultiplyAlpha:false,mipMap:false});

Using textures is easy, simply bind the texture, providing the texture unit number, then set the texture sampler unit in the shader.

// Bind to texture unit 0texture.bind(0);// Bind texture sampler to the same unit.shader.setUniform('uTextureSampler',0);// .. draw using texture// Unbind from texture unit 0.texture.unbind();

ColorTextures

Color textures are the most common type of texture and are used to store RGB or RGBA values. Anesper.ColorTexture2D can be created from existing HTMLImageElements, URLs containing HTMLImageElement supported image formats, ArrayBufferViews, or null data.

// Create texture from image URL.lettexture=newesper.ColorTexture2D({src:'images/checkerboard.png'},(err,texture)=>{if(err){console.error(err);return;}console.log('Texture2D image successfully created.');});// Create empty color texture buffer to be written to.letcolorTexture=newesper.Texture2D({height:256,width:256});

WebGL 1.0 requires that any texture with mipmapping enabled or repeating wrap modes (REPEAT orMIRRORED_REPEAT),must have dimensions that are powers of two.esper.ColorTexture2D will automatically resize any non power of two textures to the next highest power of two. Ex. 129 becomes 256, 15 becomes 16, etc.

DepthTextures

Depth textures can be used to store depth values and are commonly used in conjunction with RenderTargets. Theesper.DepthTexture2D class is only available if theWEBGL_depth_texture extension is supported.

// Create a depth texture. (Only works if depth texture extension is supported).letdepthTexture=newesper.DepthTexture2D({width:1024,height:1024});

TextureCubeMaps

Cubemap textures are a specific type of texture typically used for skyboxes and reflections. Anesper.TextureCubeMap can be created from existing HTMLImageElements, URLs containing HTMLImageElement supported image formats, ArrayBufferViews, or null data. The faces of the cube are specified during instantiation.

// Create cube map from image URLs.letcubeMapTexture=newesper.TextureCubeMap({faces:{'+x':'images/sky/posx.png','-x':'images/sky/negx.png','+y':'images/sky/posy.png','-y':'images/sky/negy.png','+z':'images/sky/posz.png','-z':'images/sky/negz.png'}},(err,texture)=>{if(err){console.error(err);return;}console.log('TextureCubeMap successfully created.');});// Create empty cube map to be written to.letcubeMapTexture=newTextureCubeMap({width:512,height:512});

Using cubemap textures is easy, simply bind the texture, providing the texture unit number, then set the texture sampler unit in the shader.

// Bind to texture unit 0.cubeMapTexture.bind(0);// Bind texture sampler to the same unit.shader.setUniform('uTextureSampler',0);// .. draw using cubemap texture// Unbind from texture unit 0.cubeMapTexture.unbind();

RenderTargets

When compositing more complex scenes, intermediate render states may need to be written to an additional buffer. Theesper.RenderTarget class provides an interface to allow you to draw to textures.

// Create a shadow map texture.letshadowTexture=newesper.ColorTexture2D({width:512,height:512});// Create a depth texture for the render target.letdepthTexture=newesper.DepthTexture2D({width:512,height:512});// Create a render target and attach the textures.letrenderTarget=newesper.RenderTarget();renderTarget.setColorTarget(shadowTexture,0);// Bind to color attachment 0renderTarget.setDepthTarget(depthTexture);

Drawing to the texture unit is simple:

// Bind the render target.renderTarget.bind();// Clear the bound color and depth textures.gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);// .. render to the render target// Unbind the the render target.renderTarget.unbind();// Now use the bound textures as you would normally.// Bind shadow map to texture unit 0.shadowTexture.bind(0);// Bind texture sampler to the same unit.shader.setUniform('uShadowTextureSampler',0);// ... render using the texture// Unbind from texture unit 0.shadowTexture.unbind();

About

A low-level ES6 WebGL rendering framework

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp