webgl triangle doesn't rotate?

175 views
Skip to first unread message

Hassan Hayat

unread,
May 5, 2014, 1:37:59 PM5/5/14
to elm-d...@googlegroups.com
Hey, I've been messing around with webgl and tried to get a triangle to rotate with time. I was wondering if someone could help.

When I print the triangle to the screen I can see that the points are moving but when I instead try to render, the triangle is just static.

Maybe I just didn't understand how everything works but I don't see what's wrong with the code.

Here is the code I wrote:

import MJS (..)
import Graphics.WebGL (..)


-- This is the main function
-- It renders the triangle then prints the points 
-- of the triangle
-- Notice how the points change but the render doesn't
main = flow down <~ ( combine [ render <~ updateTriangle
                              , print <~ updateTriangle] )


type ColorPoint = { color : V3 , point : V3 }

type Polygon = [Triangle ColorPoint]

colorToV3 : Color -> V3
colorToV3 color =
  let c = toRgb color
  in v3 (toFloat c.red   / 255) 
        (toFloat c.green / 255)
        (toFloat c.blue  / 255)
        

triangle : Color -> V3 -> V3 -> V3 -> Polygon
triangle color p1 p2 p3 = 
  let c = colorToV3 color
  in [ (ColorPoint c p1 , ColorPoint c p2 , ColorPoint c p3) ]
  

-- Function to rotate a polygon about an axis
-- The inner function do the following
--    rotM  creates the rotation matrix
--    rotPt rotates a single point using the rotation matrix
--    rotTr rotates a triangle of points
-- Therefore the function just does rotTr on all triangles
-- of the polygon 
rotateAxis : V3 -> Float -> Polygon -> Polygon
rotateAxis axis angle polygon = 
  let rotM = makeRotate angle axis 
      rotPt cp = ColorPoint (cp.color) (rotM `mul4x4`cp.point)
      rotTr (cp1 , cp2 , cp3) = (rotPt cp1 , rotPt cp2 , rotPt cp3)
  in map rotTr polygon


rotateX = rotateAxis (v3 1 0 0)
rotateY = rotateAxis (v3 0 1 0)
rotateZ = rotateAxis (v3 0 0 1)


defaultTriangle : Polygon
defaultTriangle = triangle blue (v3 1 0 0) (v3 0 1 0) (v3 -1 0 0)


scene : Polygon -> [Model]
scene tris = [ model vShader fShader tris {} ]

-- renders a polygon on the screen
render : Polygon -> Element
render = webgl (400,400) . scene

-- prints a polygon on the screen
print : Polygon -> Element
print polygon = 
  let printPoint pt = {  point = toTuple3 pt.point}
      printTriangle (p1 , p2 , p3) = 
        ( printPoint p1 , printPoint p2 , printPoint p3 )
  in asText (map printTriangle polygon)
  

getInput : Signal Float
getInput = flip (/) 100000 <~ foldp (+) 0 (fps 16)

updateTriangle : Signal Polygon
updateTriangle = foldp rotateZ defaultTriangle getInput

-------------------------------------------------------------------

vShader = [glShader| 

attribute vec3 point;
attribute vec3 color;
varying vec3 vColor;

void main(){
  gl_Position = vec4(point, 1.0);
  vColor = color;
}

|]

fShader = [glShader|

precision mediump float;
varying vec3 vColor;

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

|]


End of Code.

Thanks for your help,
Hassan 

John Mayer

unread,
May 5, 2014, 2:20:16 PM5/5/14
to elm-d...@googlegroups.com

You'll need to pass the rotation into the vertex shader. The vertex shader is responsible for mapping your vertex coordinates to the screen.

First, this requires that you change some GLSL, adding a new uniform variable that is a rotation matrix.

Something like:

...
uniform rot mat4;
...
gl_Position = rot * vec4(point, 1.0);
...

This also changes the type of the vertex shader, so now, the model function enforces that you provide the rotation value. So you will need to add a field "rot" to the fourth parameter of the model function.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Evan Czaplicki

unread,
May 5, 2014, 2:34:13 PM5/5/14
to elm-d...@googlegroups.com
As I understand it, you change your scene by sending values to the shaders.

You try to leave the model and triangles static because it's expensive to load them onto the GPU.

So in this case, you want the vertex shader to get a value that tells it how to spin the vertices, which John described and which happens in the Box and Crate examples.

Sound correct?

Hassan Hayat

unread,
May 5, 2014, 2:51:37 PM5/5/14
to elm-d...@googlegroups.com
Oh right. Thanks a lot I had completely forgotten.

I just got used to how you can just get changes by hooking up a function to a Signal so I just assumed that it would work the same way here. Turns out I have to pass in the 4 x 4 matrix with all the scaling, translation and rotation.

Basically, I assumed that since I made the triangle into a signal, the render function would just re-send the model and triangles at every time step and thus I'd get rotation.

John Mayer

unread,
May 5, 2014, 3:07:53 PM5/5/14
to elm-d...@googlegroups.com

Right. The concept here is that you don't want to perform the rotation outside of the shader, but rather inside of the shader. This is because the graphics card will parallelize the multiplications of each vertex. So each game "tick" you would send the triangles and the updated rotation, which will probably change for each "tick".

Evan points out something important. It is rather expensive to re-bind the triangles to the internal buffers of your GPU. We do some caching under the hood such that if your triangles don't change, they get re-used. However, it is relatively cheap to send in new uniforms for each game "tick". So your triangles, also known as a mesh, are best instantiated once, and then moved and rotated using uniforms which change on each "tick".

Evan Czaplicki

unread,
May 5, 2014, 3:11:45 PM5/5/14
to elm-d...@googlegroups.com
Is it a bug that the triangles don't get resent if someone asks for that? I mean, it's not the efficient way to do things, but it should be possible to do, right?

John Mayer

unread,
May 5, 2014, 3:17:34 PM5/5/14
to elm-d...@googlegroups.com

Nope not a bug. Just possibly inefficient. For example, if one built a modeling tool, then you would need to be able update a mesh directly.

Evan Czaplicki

unread,
May 5, 2014, 3:23:21 PM5/5/14
to elm-d...@googlegroups.com
It looks like Hassan's code is doing that though, but not seeing any updates.

I may not be understanding things right, but it looks like he is transforming a [Triangle ColorPoint] and sending a fresh one each time. I'd expect the triangle to rotate, but with the performance problems we described. It appears that it's just not moving at all though. Is that a correct assessment?

John Mayer

unread,
May 5, 2014, 3:45:45 PM5/5/14
to elm-d...@googlegroups.com

Oh yeah, weird and yes I missed that. Still just on my phone. I guess we can't yet put it on share-elm?

I'll be home soon to take a look.

Hassan Hayat

unread,
May 5, 2014, 6:59:21 PM5/5/14
to elm-d...@googlegroups.com
Yeah, that's basically it.

At every "tick", the getInput function creates a new angle (this angle starts at 0 and then just goes up). This angle then is used in the updateTriangle function which creates a new triangle from a previous triangle by rotating it by that angle and returning the result. The base case is defined by "defaultTriangle" and then the rotation gets accumulated with "foldp". Each resulting triangle is just freshly created in the "rotateAxis" function. 

So, basically, at every "tick", the program creates a new model with the same shaders but with a different triangle (defined by different points). And then I send that model to "webgl". (that's basically what the "render" function does. it constructs the model and passes it on to "webgl")

I hope this explanation helps and I'm willing to clarify anything that needs clarifying.

I agree, this is inefficient and I think that this sort of code is why the educational system invented F's but I sort of expected (from how I've seen that Signals work) that this would just flush any buffers that need flushing and clear anything that needs clearing and just re-send the shaders and all the triangles and re-draw everything. 

R Britten

unread,
May 7, 2014, 12:08:59 AM5/7/14
to elm-d...@googlegroups.com, john.p....@gmail.com
Definitely an essential feature, I think.  Our example from 2012 required deforming the mesh interactively, and an example we are working on now (albeit just using JavaScript directly) animates a deformation.

John Mayer

unread,
May 7, 2014, 8:06:21 AM5/7/14
to elm-d...@googlegroups.com

I've opened an issue on github and will fix this

Evan Czaplicki

unread,
May 14, 2014, 10:09:09 PM5/14/14
to elm-d...@googlegroups.com
Okay, should be fixed now! The updated code looks like this.
Reply all
Reply to author
Forward
0 new messages