WebGL and 2D: Simple as Web

without GL

WebGL 2D: Simple as Web

Korotaev Aleksandr

  • Front-end developer at Tinkoff.ru
  • Spb-frontend co-organizer
  • making games at home and office

web-standards-ru/cfp-list

Fintech? Games?

WebGL
pure magic

WebGl is a triangle renderer

9 years of

WebGL

  • version 1.0
  • version 2.0
  • WebGPU ?

API not from Web

        gl.activeTexture(gl.TEXTURE0)
        gl.bindTexture(gl.TEXTURE_2D, texture)
        gl.texImage2D(this.gl.TEXTURE_2D, ..., image)
        gl.uniform1i(gl.getUniformLocation('u_image'), 0);
    
https://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences

API not from Web

        setState(arg1)
        setState(arg1, arg2)
        setState(arg1, ..., arg9)
        // but you can't
        let state = getState()
    

WebGL summary

New JS library?

Canvas API -> WebGL

gameclosure/webgl-2d

Nobody should understand WebGL

But I tried...

Demo
https://webglfundamentals.org/webgl/lessons/webgl-image-processing.html

Algorhytm

First Shader

        uniform sampler2D u_background;
        varying vec2 v_texCoord;
         
        void main() {
          gl_FragColor = texture2D(u_background, v_texCoord);
        }
    

Need more images!

+ =

2 textures in one shader

        uniform sampler2D u_background;
        uniform sampler2D u_foreground;
        varying vec2 v_texCoord;
        void main() {
          gl_FragColor = texture2D(u_background, v_texCoord)
              + texture2D(u_foreground, v_texCoord);
        }
    

+
=

*
=

/
=
Shaders
when you need effects

Evil Glitch

Link to the game

About <script>

Use Webpack

        rules: [
        ...
          {
              test: /\.frag?$/,
              use: 'raw-loader',
          }]
    

😍😍😍

        import * as fragmentShader from './shader.frag'
        import * as vertexShader from './shader.vert'
    

WebGL utils

https://webglfundamentals.org/docs/module-webgl-utils.html
        const shaders = [vertexShader, fragmentShader]
        const program = createProgramFromSources(gl, shaders)
    

Shaders links

Demo

React
+ WebGL

Let`s Google it

Why Pixi.js?





https://codesandbox.io/s/q7oj1p0jo6

Demo

React-pixi-fiber

        <Container>
            {state.enemies.map(e => (<Enemy {...e}/>))}
            {state.explosions.map(e => (<Explosion {...e}/>))}
            <Actor {...state.hero}/>
            <Header x={0} y={0} text={state.kills}/>
        <Container>
    

React-pixi-fiber

Ready to production?

How it looks

React-pixi-fiber

❤️ Demo for at least one day
❤️ Adaptive
❤️ Retina

Game as interface

        <Clickable onClick={_=>this.setState({screen: 'game'})}>
          <Text text="Play" />
        </Clickable>
         
        {this.renderScreen(this.state.screen)}
    

Retina

        const OPTIONS = {
          backgroundColor: color('#12bcf6'),
          autoResize: true,
          resolution: window.devicePixelRatio || 1,
        };
    

Adaptive

        <Button width={clamp(props.width * .6, 100, 200)}>
    
        Button {
          width: 60%;
          min-width: 100px;
          max-width: 200px;
        }
    

SVG

It renders as a raster image and sizes should be doubled

        <svg width="100" height="100" 
          viewBox="0 0 50 50" ... >
          ...
        </svg>
    
Runners
and 60 FPS story

setState() 60 FPS

        componentDidMount() {
          this.props.app.ticker.add(this.onTick);
        }
         
        onTick() {
          this.setState({enemies, actor, time...})
        }
    

Avoid setState()


pixi-viewport

Custom component

            import {CustomPIXIComponent} from 'react-pixi-fiber';
            import Viewport from 'pixi-viewport';
             
            const TYPE = 'Viewport';
            const behavior = {
              customDisplayObject: props => {
                return new Viewport({
                  screenWidth: props.app.renderer.width,
                  screenHeight: props.app.renderer.height,
                  worldWidth: 1000,
                  worldHeight: 1000,
                })
              },
            };
            
            export default CustomPIXIComponent(behavior, TYPE);
        

Custom component

        <Viewport app={app}>
          {/* add display objects here */}
        </Viewport>
    

Different viewports

            <MountainsViewport app={app}>
              <Mountains />
            </MountainsViewport>
            <PlatformsViewport app={app}>
              {platforms.map(p => (<Platform {...p}/>))
            </PlatformsViewport>
            <ActorViewport app={app}>
              <Actor />
            </ActorViewport>
        

Like iframe

React + Pixi

🏅 fast prototypying
😍 developer experience
🍷 well-known React-stack
React
+ shaders

Shader Component

        export class Filter extends PureComponent {
          constructor() {
              const filter = new PIXI.Filter(
                  null, this.props.shader, this.uniforms)
              
              this.setState({filters: [filter]})
          }
        }
    

Shader Component

        export class Filter extends PureComponent {
          render() {
            return (
              <Container filters={this.state.filters}>
                 {this.props.children}
              </Container>
          )}
        }
    

Shader Component

        import fragmentShader from '../shaders/shader.frag'
          
        <Filter shader={fragmentShader}>
          ...children...
        </Filter>
    

How it looks

Get shaders from Google search?

Shader from the search

GLSL 3.0 support

In WebGL 2.0 only

GLSL 3.0 + Pixi

Broken 😡

pixi.js/issues/5752

GLSL 3.0 detection

GLSL 3.0 to 1.0

The only way...

Key
takeaways

Draws faster?

Effects?

Links