webgl-fundamentals icon indicating copy to clipboard operation
webgl-fundamentals copied to clipboard

Perspective lesson, would like to stay in top/left coordinates DOM-style coordinates.

Open trusktr opened this issue 7 years ago • 8 comments

I just finished the perspective lesson, but unfortunately when going from the shader math to perspective matrix, the coordinate system changed from top/left to center/center, and opposite direction for Y axis.

I would really love to keep it in the same coordinate system as with DOM, because one of my goals is to overlay DOM with WebGL and move them around in the same 3D space. This would be similar to (the now abandoned) Famous "Mixed Mode" if you've heard of that. It is also similar to these examples:

  • http://jeromeetienne.github.io/videobrowser4learningthreejs
  • http://learningthreejs.com/data/2013-04-30-closing-the-gap-between-html-and-webgl/index.html
  • http://rusthon.herokuapp.com
  • http://deathcap.github.io/gl-css3d/
  • https://www.pubnub.com/blog/2015-01-08-webgl-dom-and-physics-w-famo-us-mixed-mode
  • http://jsfiddle.net/d2366c5n/1
  • https://github.com/voxel/voxel-webview

(inspect to see the DOM)

What I am aiming for is to easily make 3D content using the same coordinate system as DOM, so that I can decorate my DOM (f.e. I'd like to make some real-time shadows on a button or something).

So I was excited when the first tutorials were doing everything in top/left coordinates, with Y increasing downward, like DOM! :D

But then BAM, use this perspective matrix, no more DOM-like coordinates.

So I'd like to play with this so I can make my perspective matrix work like DOM.

I'm also going to have to figure out how CSS perspective property works, to make that also work the same in WebGL. What I've noticed is that if you set perspective:800px in CSS, then that means that an element inside that space can move towards the screen until 800px, and then it disappears. But when you move objects away from the screen (negative Z direction) then they seem to be able to move infinitely away, never clipped. That's something I'll have to figure out too (how to keep things never clipped, at least not until very very far).

If you have tips or if you know how to change the turorial to still use DOM coordinates, I'd be glad to hear. :)

trusktr avatar May 21 '17 22:05 trusktr

Another thing to note is that I'd like to also match DOM CSS pixels to WebGL pixels. So I'd like for it to be that when a WebGL quad is at position (0,0,0) that it's top left corner will be at the top left of the screen, and it's size in pixels will match that of a DOM element at the same position with no transforms.

trusktr avatar May 21 '17 23:05 trusktr

It really makes no sense use DOM coordinates in 3D for 99.9999% of all projects so changing the tutorial is probably not the best thing to do.

The problem is you (you the developer) need to choose somewhere in 3D space where the DOM would match the world since perspective will move things apart when getting toward the camera and together when moving away. Is that 1 unit from the camera, 10 units from the camera, 100.123 units from the camera?

Also, while the perspective function gives you a matrix looking down the center of the screen you can also make matrix that's off center which is yet another arbitrary decision for a developer to make.

If you want the same math as CSS then read through the CSS spec. It should spell out what matrix math to use.

Maybe another tutorial about matching CSS at some point.

Otherwise some other related things

  • position html to match the 3d

    https://webglfundamentals.org/webgl/lessons/webgl-text-html.html

  • use one large canvas to emulate lots of small canvas

    http://twgljs.org/examples/itemlist.html

greggman avatar May 22 '17 02:05 greggman

It really makes no sense use DOM coordinates in 3D for 99.9999% of all projects so changing the tutorial is probably not the best thing to do.

I agree it doesn't need to be in the docs per se, but I'd like to do it anyways. I'm gonna try to figure it out.

In my lib (infamous), I want it to be as easy as HTML to make UIs (with WebGL) in a way that HTML developers are already familiar with (same coordinate system), so out of the box I'd like to have WebGL coordinates to be identical to DOM coordinates.

Thanks to your lessons, yesterday and today I added initial super-basic WebGL rendering to my lib (it's messy, first draft), so now what I've learned in your tutorials is taking advantage of the "scene graph" API that I already built in infamous! 😄 So basically I took what I have so far from the tutorials, and I made it traverse my scene graph in the draw function.

To make a scene graph with infamous, there's an imperative JavaScript API that looks like

import {Motor, Scene, Node} from 'infamous/motor'

const scene = new Scene

const node1 = new Node({
  position: [20,20,20],
  rotation: [30,30,30],
})

scene.addChild(node1)

const node2 = new Node({
  position: [100,200,300],
  rotation: [30,20,10],
})

node1.addChild(node2)

Motor.addRenderTask(time => {
  node1.rotation.x++
  node2.rotation.y++
  // or do some other stuff based on time like a tween transition, physics, etc.
})

but the cool thing is there's also a declarative API! So we can write the same scene with HTML,

<motor-scene>
  <motor-node id="node1"  
    position="20 20 20"
    rotation="30 30 30"
  >
    <motor-node id="node2"
      position="100,200,300"
      rotation="30,20,10"
    >
    </motor-node>
  </motor-node>
</motor-scene>

then we can manipulate the scene with JavaScript

import {Motor} from 'infamous/motor'

const node1 = document.querySelector('#node1')
const node2 = document.querySelector('#node2')

Motor.addRenderTask(time => {
  node1.rotation.x++
  node2.rotation.y++
  // or do some other stuff based on time like a tween transition, physics, etc.
})

and you can also imagine manipulating the DOM with React, Angular, etc.

Here's a basic React example and screenshot (with hard coded lights, camera, and cube mesh for each node, for now):

import * as React from 'react'
import 'infamous/motor-html'

export default
class App extends React.Component {

    render() {
        return (
            <motor-scene>
                <motor-node
                    ref='node1'
                    id='node1'
                    absoluteSize='100, 100'
                    rotation='30, 30, 30'
                    //align='0.5, 0.5'
                    //mountPoint='0.5, 0.5'
                >
                    <motor-node
                        ref='node2'
                        id='node2'
                        absoluteSize='10, 10'
                        rotation='90, 90, 20'
                        //align='0.5, 0.5'
                        //mountPoint='0.5, 0.5'
                        position='100, 100, 100'
                    >
                    </motor-node>
                </motor-node>
            </motor-scene>
        )
    }
}

screenshot 2017-06-14 at 10 36 57 pm

I'll post a live example later!

trusktr avatar Jun 15 '17 04:06 trusktr

Oh, and I forgot to mention that the transforms are instances of DOMMatrix which are part of the new geometry w3c spec landing soon in Chrome and already released in Safari tech preview.

What I'm trying to figure out, is when I apply the local transform of each node to the DOM as a CSS transform:matrix3d() and then also draw a WebGL cube for each node (after calculating world matrices for each node), why the rotation on X and Y axis is visually opposite than with the WebGL rendering.

hmmm..

I'm still trying to iron some other things out to. For example, in the world matrix calculation traversal, if I do it like

                //this._worldMatrix = parent._worldMatrix.multiply(this._properties.transform)
                this._worldMatrix = this._properties.transform.multiply(parent._worldMatrix)

then it renders as the above screenshot, but if I try

                this._worldMatrix = parent._worldMatrix.multiply(this._properties.transform)
                //this._worldMatrix = this._properties.transform.multiply(parent._worldMatrix)

in hopes that maybe that fixes the reverse problem, then vertices explode for the child node:

screenshot 2017-06-14 at 11 49 23 pm

I guess I have more to learn! 😄

trusktr avatar Jun 15 '17 05:06 trusktr

Anyways, don't worry about debugging my custom code. 😆 I'll post an live example later if you're interested to see how it works, and seeing how it renders with the vertex explosion when the matrices are multiplied in the opposite order.

trusktr avatar Jun 15 '17 05:06 trusktr

Another thing that is weird, is that in the WebGL output, the Z axis rotation always happens relative to the parent Z axis, whereas X and Y rotation happens relative to the actual object.

Is anything like this expected? I have a feeling I have a bug somewhere. WebGL is so hard to debug too!!!

trusktr avatar Jun 16 '17 17:06 trusktr

Ah, I see!! It is because Z rotation is relative to the parent, then the Y axis for rotation rotates around the Z axis when we rotate around Z, and the X axis rotates around the Y axis when we apply rotation around Y, and finally, the object rotates around the X axis when we rotate around X.

I suppose I need to figure out which order I want these rotations to happen in.

Do you have any recommendation on what works best for you @greggman ?

trusktr avatar Jun 16 '17 17:06 trusktr

Here's a couple live samples using my lib (with what I've learnt from here so far! :smile:):

  • https://trusktr.io/geometricRotation
  • https://trusktr.io/worms

The source code for those are written with React, manipulating HTML elements to create the scene graphs:

Note the color and mesh attributes cause the motor-nodes to be rendered, otherwise the motor-nodes only hold transforms for the hierarchy without being rendered.

The infamous source around WebGL is a super messy rough draft (although getting better), don't look! Next I'll come up with a way to organize uniforms and attributes, etc.

trusktr avatar Jun 17 '17 23:06 trusktr