Giter Site home page Giter Site logo

elm-webgl's Introduction

Note: The primary fork of this project can be found at elm-community

WebGL for Elm

A simple API for rendering with WebGL. This is useful for both 2D and 3D rendering because it lets you take advantage of hardware acceleration with the GPU, meaning you can render things more quickly.

Here are some examples so you can get a feel for the API, but make sure you read on to learn how WebGL and the GPU really work!

Triangle Cube Crate Thwomp FirstPerson

Understanding WebGL

To get the most out of this library and out of the GPU, it is best to pair some examples with a fairly solid understanding of how information flows through the rendering pipeline. This section gives a high-level overview of the pipeline and the corresponding terminology.

At a high-level, there are two general concepts to understand: meshes and shaders. The details of each of these are crucial to using WebGL effectively.

Meshes

A mesh is all about triangles. By placing small triangles side-by-side, you can build up larger 3D shapes. We define each triangle by associating a bunch of attributes—like position and color—with each corner of the triangle.

We create and update our meshes on the CPU, so working with a model does not get any direct benefits from the GPU. Meshes are sent from the CPU to the GPU to be rendered. This transfer can be quite expensive, so it is best to try to avoid creating new meshes.

Some tricks to minimize this include breaking a mesh up into many smaller pieces that can be transformed independently. For example, if you want to render a skeleton, each bone could be a separate mesh, so rather than send a new version of the entire skeleton on every frame, you just send a transformation for each bone.

Shaders

A shader is all turning meshes into pictures. A shader is a program that runs on the GPU, so it benefits from lots of parallelization. As a general rule, you want to be doing computation here rather than on the CPU if possible.

In Elm, shaders are defined with a language called GLSL. These are programs that take in small high-level values and do a bunch of rendering based on that. For example, you can send over a matrix that represents where the camera should be and all of the meshes loaded onto the GPU will be transformed accordingly.

Combining Meshes and Shaders

The following diagram illustrates the entire pipeline. Keep reading past the diagram, all the terms will be explained!

WebGL Pipeline

We start with a mesh. It's a bunch of raw data points that we want to render on screen. From there, the data flows through two types of shaders:

  • Vertex Shaders — Our mesh is made up of lots of triangles. Each corner of a triangle is called a vertex. The vertex shader has access to all of the attributes of each vertex, like position and color, letting us move triangles around or change their color.

  • Fragment Shaders — Also known as pixel shaders, these shaders are like filters on individual pixels. They let you work with pixels to add lighting effects or add postprocessing effects like blur or edge-detection.

The flow of data between the CPU and each of our shaders is very well defined. To send information, there are three kinds of specialized variables:

  • Uniform — these are global read-only variables that can be used in both the vertex and fragment shaders. They are defined on the CPU.

  • Attribute — these variables represent a particular vertex in our mesh. The vertex shader takes in these variables to compute some transformations on each vertex.

  • Varying — these are variables you can write in the vertex shader which then get passed along into the fragment shader, where they are read-only. This lets you pass information along as you compute things in your rendering pipeline.

Making the most of the GPU

A typical mesh may be quite large, with hundreds or thousands of vertices, each with a potentially large set of attributes. Working with a mesh on the CPU is expensive for two major reasons:

  1. The CPU is sequential, so you must work on each vertex one at a time. On the GPU, you can work with tons of vertices in parallel with a Vertex Shader, making things much faster.

  2. Transfering data from CPU to GPU is expensive. Ideally you want to transfer all of the vertices once and make any updates to the mesh by passing in uniform variables to the Vertex Shader. Not only is it cheaper to send a couple matrices to the GPU, but once they get there, the GPU can use them in parallel.

This library facilitates this by caching known meshes on the GPU. If you create a mesh and use it many times, it only gets transfered to the GPU once. Now if you update that mesh, the new version will need to be loaded onto the GPU separately. That means it is best to create a fairly general mesh and modify it with uniform variables in a Vertex Shader.

Writing Shaders

Shaders are written in a language called GLSL. This is a widely used language for shaders with websites devoted to sharing creative demos, so you will often be able to use that shader code directly in Elm. A basic vertex shader could be defined like this:

vertexShader : Shader { position:Vec3, coord:Vec3 } { u | view:Mat4 } { vcoord:Vec2 }
vertexShader = [glsl|

attribute vec3 position;
attribute vec3 coord;
uniform   mat4 view;
varying   vec2 vcoord;

void main () {
  gl_Position = view * vec4(position, 1.0);
  vcoord = coord.xy;
}

|]

Within the [glsl| ... |] block, you just write plain GLSL. Elm is actually aware of the types of the attributes, uniforms, and varyings coming in and out of the shader, so the shader block is given a type that enforces this API. The type of vertexShader says that we have two attributes named position and coord, one uniform named view, and one varying called vcoord. This means Elm's type checker can make sure you are using the shader in a meaningful way, avoiding a totally blank screen that can happen if your shader has an error in it.

elm-webgl's People

Contributors

aforemny avatar deadfoxygrandpa avatar glendc avatar jaspertron avatar jdavidberger avatar johnpmayer avatar jvoigtlaender avatar process-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

elm-webgl's Issues

Request: API similar to sendGet for Textures

Is it possible to have a function like loadTexture which takes as input a Signal String instead of a String?

The problem is that right now, it's impossible to load textures from anything not hard-coded into the file. If I get a texture URL from a user or (more likely) a remote model file, then the URL is of type Signal String, so when I lift it to loadTexture I get Signal (Signal String) back, which can't be used.

The HTTP library gets around this by taking a signal as input. The potential downside, though, is that it would then have to update the texture whenever the signal changed.

Need Access to RenderingContext to change blendmode

I'm making a small 2d game on top of webgl - which requires a change in blend-mode to respect the transparency of the pngs I'm using as a texture:

gl.enable( gl.BLEND );
gl.blendEquation( gl.FUNC_ADD );
gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );

I'm totally happy to do the work to add hooks to let the user configure this, but I'm not 100% sure how that API should look on the Elm side.

Is there any interest in exposing this sort of configuration, and suggestions on how that should look on the elm side?

Package fails to install and reports missing elm-package.json

When I try to install the package (using Elm 0.15) I get this error:

[vagrant@localhost game]$ elm-package install johnpmayer/elm-webgl
To install johnpmayer/elm-webgl I would like to add the following
dependency to elm-package.json:

    "johnpmayer/elm-webgl": "1.0.1 <= v < 2.0.0"

May I add that to elm-package.json for you? (y/n) y

Error: Unable to get elm-package.json for johnpmayer/elm-webgl 1.0.1
Missing field "elm-version", acceptable versions of the Elm Platform (e.g. "0.15.0 <= v < 0.16.0").
    Check out an example elm-package.json file here:
    <https://raw.githubusercontent.com/evancz/elm-html/master/elm-package.json>

Is the published package missing this file (the dependencies all installed ok manually)?

Add support for passing arrays as uniforms

There is a design choice here on whether to use Elm Arrays or Elm Lists to do this, but, the essence is that you'd want the following to be possible:

uniform vec3 myVectorArray[MY_VECTOR_ARRAY_LENGTH];

In GLSL, uniform arrays must be of fixed size and that size must be declared at compile-time and therefore the length of the array must be computed at run-time in order to compile the program. This could trigger dynamic recompilation of the shader if the Array were to change size.

Examples linked in README don't work

I get the following error: result:578 Uncaught Error: 'main' is missing! What do I display?!

Using Mac OS X 10.11.1 with Google Chrome Canary Version 49.0.2579.0 canary (64-bit). Same on desktop Safari.

Links to the examples in the README

Just noting. I'd like to try this package, but all the examples seem to have gone down. I can't find any links to the code either, which makes experimentation hard.

Mouse.position change not reflected

Hi,

Finally found some time to play with your webgl library and it is great.

I found an issue, not sure if it is a webgl-elm or elm issue but with the code below I got the following behavior
http://ibin.co/1qp7HBTcTGBJ

where the red arrow indicate the mouse pointer position.

This happen when I move the mouse pointer suddenly.
If I move the pointer again it still reset to the correct behaviour:
the screen is filled from the top left corner (As it should)

It seems like there is an issue with webgl not being updated properly after the signal from the Mouse position

import Mouse
import Window
import Signal (map, map2)
import Text (asText)
import WebGL as GL
import Math.Vector3 (..)
import Math.Vector2 (..)
import Math.Matrix4 as Mat4
import Graphics.Element (..)

main = map view Mouse.position 


type alias Vertex = { position : Vec2, color : Vec3 }

mesh : List (GL.Triangle Vertex)
mesh =
    [ ( Vertex (vec2 0  0 ) (vec3 1 0 0)
      , Vertex (vec2 1  1 ) (vec3 0 1 0)
      , Vertex (vec2 1 0 ) (vec3 0 0 1)
      ),
      ( Vertex (vec2 0  0 ) (vec3 1 0 0)
      , Vertex (vec2 0  1 ) (vec3 0 0 1)
      , Vertex (vec2 1 1 ) (vec3 0 1 0)
      )
    ]


ortho2D w h = Mat4.makeOrtho2D 0 w h 0



view : (Int,Int)  -> Element 
view (w,h) =
  let matrix = ortho2D 1 1
  in GL.webgl (w,h)
    [ GL.entity vertexShader fragmentShader mesh { mat = matrix } ]


-- Shaders

vertexShader : GL.Shader { attr | position:Vec2, color:Vec3 } { unif | mat:Mat4.Mat4 } { vcolor:Vec3 }
vertexShader = [glsl|

attribute vec2 position;
attribute vec3 color;
uniform mat4 mat;
varying vec3 vcolor;

void main () {
    gl_Position = mat * vec4(position, 0.0, 1.0);
    vcolor = color;
}

|]


fragmentShader : GL.Shader {} u { vcolor:Vec3 }
fragmentShader = [glsl|

precision mediump float;
varying vec3 vcolor;

void main () {
    gl_FragColor = vec4(vcolor, 1.0);
}

|]

Unable to install elm packages for examples

When trying to run the examples locally on OS X, I am unable to install the elm packages required for the examples.

➜  examples git:(master) ✗ elm package   
elm package 0.16.0

…

➜  examples git:(master) ✗ elm package install

Error: Unable to find a set of packages that will work with your constraints.

Elm 0.17 support

Now that Elm 0.17 is released, what is the timeline like for supporting this platform version?
Would the migration be as simple as bumping the version in elm-package or would there be conflicts?

Remove console.log("Drawing") ?

Native/Graphics/WebGL.js:    console.log("Drawing");

in the library causes thousands of lines of logging, which gets in the way of the user's own console messages. Could it be removed?

Readme.md typo

"This is useful for both 2D and 3D rendering because it lets you take advantage of hardware acceleration with the GPU, meaning you can _rendering_ things more quickly."

I think "rendering" should rather be "render".

port Task WebGL.Error Texture

from the examples:

texture : Signal.Mailbox (Maybe Texture)
texture =
  Signal.mailbox Nothing

port textureFetcher : Task WebGL.Error ()
port textureFetcher =
  loadTexture "/texture/woodCrate.jpg"
    `Task.andThen` \tex -> Signal.send texture.address (Just tex)

causes this issue:

Object is not a function (evaluating 'fun(a)(b)')

generated js:

   var texture = $Signal.mailbox($Maybe.Nothing);
   var textureFetcher = Elm.Native.Task.make(_elm).perform(A2($Task.andThen,
   $Task.toMaybe($WebGL.loadTexture("/texture/woodCrate.jpg")),
   function (tex) {
      return A2($Signal.send,
      texture.address,
      tex);
   }));

GLSL parse error on shader containing ||

I'm playing with a shader from http://www.geeks3d.com/20130524/building-worlds-with-distance-functions-in-glsl-raymarching-glslhacker-tutorial-opengl/2/ in a [glshader| quote block, and it gets a parse error on the line:

  if ((abs(d.x) < .001) || (f > maxd)) 

The error is:

"GLSL" (line 78, column 29):
unexpected "|"
expecting "=", "/", space, "++", "--", "+", "-", "!", "~", letter, "_", ...

Playing around a bit further it seems that && is not recognized either. Probably all the operators listed in http://www.khronos.org/files/opengl-quick-reference-card.pdf should be included.

Texture loading does not work, causes runtime error "fun(...) is not a function"

loadTextureWithFilter is defined in WebGL.elm as

loadTextureWithFilter filter url = Native.WebGL.loadTextureRaw Linear url

However, Native.WebGL.loadTextureRaw only takes one argument, that being the URL. Since elm tries to apply the return value of Native.WebGL.loadTextureRaw, which isn't a function, to Linear, this results in a JS type error at runtime, displaying "fun(...) is not a function" on screen (in firefox anyhow).

Semi-relatedly, it appears that Native.WebGL.loadTexture is no longer used anywhere.

Add/Test/Change native support for passing arrays of Entity to webgl

So, while I was working on Graphics Engine (my 3d engine based on elm-webgl), I realized that Lists were very slow as an internal representation for my scene objects. Switching to Array gave me a HUGE performance boost (which was kinda unexpected cuz I tested on pong, which has just 4 scene objects: 2 paddles, 1 ball, and 1 background).

But to do that, I have to call toList at one point so that I may pass the data onto webgl as it expects a List Entity as its argument.

It would be nice if there were a version where I could pass an Array Entity instead. I'm not saying this to save me from writing one word. I'm wondering if this would bring a nice speed-up to elm-webgl. At the very least, it would be nice to test the two functions side by side on the same exact code to see if there's a noticeable difference.

Rename zipTriangle to mapTriangle2?

In the spirit of zip being replaced with map in various places, how do you feel about changing zipTriangle to mapTriangle2?

I'm in the process of upgrading stuff for 0.14 to get the website all in working order, so if you are down with this, I can make the change easily myself.

Add support for lines

2D lines are useful for axes and grids. I've looked through the library and I think this is possible without changing the compiler or a breaking change. I think you could

  • Add a function line : Color -> Vec3 -> Vec3 -> Entity (or maybe List Vec3). The JS object produced would be different from that for a mesh, but in Elm it's an opaque type.
  • Add a case to drawEntity to handle this alternate construct. One of many examples online is here.

You could probably do points in a similar fashion (useful for dense grids without cluttering things visually).

Do you think this is reasonable and feasible? Thanks.

Add support for passing structs as uniforms

Suppose the following Elm type:

type alias Light = {
  position   : Vec3,
  rotation   : Vec3,
  color      : Vec3,
  intensity : Float,
  visibility  : Bool 
}

and the following GLSL struct definition

struct Light {
  vec3 position;
  vec3 rotation;
  vec3 color;
  float intensity;
  bool visibility;
};

It should be possible to pass in the elm object light : Light as a uniform to uniform Light light;.

This behavior should be true of any Elm record type and any GLSL struct type as long as the types and property names of the Elm record type match those of the GLSL struct type and that the Elm record type contains only properties with types GLSL can understand (or other types which themselves are record properties with properties with types GLSL can understand or... so on... n-levels deep.)

For example, the following is a list (non-exhaustive) of types that cannot be passed to GLSL as uniforms:

  • String
  • Char
  • Signal
  • Maybe
  • Any Union Type
  • Any Record Type that contains fields/properties with a type that cannot be passed as GLSL uniforms

The following is a list of types that can be passed to GLSL as uniforms:

  • Elm types that match basic GLSL types such as Float or Vec3
  • Record types containing fields with Elm types that either match GLSL types or Record types containing fields with Elm types that either match GLSL types or Record types ... so on...

All of the following should be able to be passed as a uniform:

type alias A = {
  a : Vec3
}

type alias B = {
  b1 : A,
  b2 : Float
}

type alias C = {
  c1 : Mat4,
  c2 : A,
  c3 : B
}

and should match the GLSL structs:

struct A {
  vec3 a;
};

struct B {
  A b1;
  float b2;
};

struct C {
  mat4 c1;
  A c2;
  B c3;
};

Mysterious variable?

I am getting an error on this line and I can't really figure out why we'd want to set model as some sort of global variable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.