html5 game dev with three.js - hexgl

Post on 15-Jan-2015

6.740 Views

Category:

Technology

5 Downloads

Preview:

Click to see full reader

DESCRIPTION

These are the slides of my talk about HexGL at the Adobe User Group meetup in the Netherlands. More info: http://bkcore.com/blog/general/adobe-user-group-nl-talk-video-hexgl.html

TRANSCRIPT

The making of HexGL

• Thibaut Despoulain (@bkcore – bkcore.com)

• 22 year-old student in Computer Engineering

• University of Technology of Belfort-Montbéliard (France)

• Web dev and 3D enthousiast

• The guy behind the HexGL project

• Fast-paced, futuristic racing game

• Inspired by the F-Zero and Wipeout series

• HTML5, JavaScript, WebGL (via Three.js)

• Less than 2 months

• Just me.

• JavaScript API

• OpenGL ES 2.0

• Chrome, FireFox, (Opera, Safari)

• <Canvas> (HTML5)

• Rendering engine

• Maintained by Ricardo Cabello (MrDoob) and Altered Qualia

• R50/stable

• + : Active community, stable, updated frequently

• - : Documentation

• First « real » game

• 2 months to learn and code

• Little to no modeling and texturing skills

• Physics? Controls? Gameplay?

• Last time I could have 2 months free

• Visibility to get an internship

• Huge learning opportunity

• Explore Three.js for good

• Third-party physics engine (rejected)

– Slow learning curve

– Not really meant for racing games

• Ray casting (rejected)

– Heavy perfomance-wise

– Needs Octree-like structure

– > too much time to learn and implement

• Home-made 2D approximation

– Little to no learning curve

– Easy to implement with 2D maps

– Pretty fast

– > But with some limitations

• Home-made 2D approximation

– No track overlap

– Limited track twist and gradient

– Accuracy depends on map resolution

– > Enough for what I had in mind

• No pixel getter on JS Image object/tag

• Canvas2D to the rescue

Load

ing Load data

texture with JS Image object

Dra

win

g Draw it on a Canvas using 2D context

Get

tin

g Get canvas pixels using getImageData()

• ImageData (BKcore package)

– Github.com/Bkcore/bkcore-js

var a = new bkcore.ImageData(path, callback);

//…

a.getPixel(x, y);

a.getPixelBilinear(xf, yf);

// -> {r, g, b, a};

Game loop:

Convert world position to pixel indexes

Get current pixel intensity (red)

If pixel is not white:

Collision

Test pixels relatively (front, left, right)

:end

Front

Left Right

Track Void

Front

Left Right

Height map

Gradient

Front

Current

Tilt

Left

Right

Three.js JSON model

Python converter

Model.OBJ

Materials.MTL

$ python convert_obj_three.py -i mesh.obj -o mesh.js

var scene = new THREE.Scene();var loader = new THREE.JSONLoader();

var createMesh = function(geometry){

var mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial());mesh.position.set(0, 0, 0);mesh.scale.set(3, 3, 3);scene.add(mesh);

};

loader.load("mesh.js", createMesh);

var renderer = new THREE.WebGLRenderer({

antialias: false,

clearColor: 0x000000

});

renderer.autoClear = false;

renderer.sortObjects = false;

renderer.setSize(width, height);

document.body.appendChild(renderer.domElement);

renderer.gammaInput = true;

renderer.gammaOutput = true;

renderer.shadowMapEnabled = true;

renderer.shadowMapSoft = true;

• Blinn-phong

– Diffuse + Specular + Normal + Environment maps

• THREE.ShaderLib.normal

– > Per vertex point lights

• Custom shader with per-pixel point lights for the road

– > Booster light

var boosterSprite = new THREE.Sprite(

{

map: spriteTexture,

blending: THREE.AdditiveBlending,

useScreenCoordinates: false,

color: 0xffffff

});

boosterSprite.mergeWith3D = false;

boosterMesh.add(boosterSprite);

var material = new THREE.ParticleBasicMaterial({

color: 0xffffff,

map: THREE.ImageUtils.loadTexture(“tex.png”),

size: 4,

blending: THREE.AdditiveBlending,

depthTest: false,

transparent: true,

vertexColors: true,sizeAttenuation: true

});

var pool = [];

var geometry = new THREE.Geometry();

geometry.dynamic = true;

for(var i = 0; i < 1000; ++i)

{

var p = new bkcore.Particle();

pool.push(p);

geometry.vertices.push(p.position);

geometry.colors.push(p.color);

}

bkcore.Particle = function()

{

this.position = new THREE.Vector3();

this.velocity = new THREE.Vector3();

this.force = new THREE.Vector3();

this.color = new THREE.Color(0x000000);

this.basecolor = new THREE.Color(0x000000);

this.life = 0.0;

this.available = true;

}

var system = new THREE.ParticleSystem(

geometry,

material

);

system.sort = false;

system.position.set(x, y, z);

system.rotation.set(a, b, c);

scene.add(system);

// Particle physics

var p = pool[i];

p.position.addSelf(p.velocity);

//…

geometry.verticesNeedUpdate = true;

geometry.colorsNeedUpdate = true;

• Particles (BKcore package)

– Github.com/BKcore/Three-extensions

var clouds = new bkcore.Particles({

opacity: 0.8,

tint: 0xffffff, color: 0x666666, color2: 0xa4f1ff,

texture: THREE.ImageUtils.loadTexture(“cloud.png”),

blending: THREE.NormalBlending,

size: 6, life: 60, max: 500,

spawn: new THREE.Vector3(3, 3, 0),

spawnRadius: new THREE.Vector3(1, 1, 2),

velocity: new THREE.Vector3(0, 0, 4),

randomness: new THREE.Vector3(5, 5, 1)

});

scene.add(clouds);

// Game loop

clouds.emit(10);

clouds.update(dt);

• Built-in support for off-screen passes

• Already has some pre-made post effects

– Bloom

– FXAA

• Easy to use and Extend

• Custom shaders

var renderTargetParameters = {

minFilter: THREE.LinearFilter,

magFilter: THREE.LinearFilter,

format: THREE.RGBFormat,

stencilBuffer: false

};

var renderTarget = new THREE.WebGLRenderTarget(

width, height,

renderTargetParameters

);

var composer = new THREE.EffectComposer(

renderer,

renderTarget

);

composer.addPass( … );

composer.render();

• Generic passes– RenderPass

– ShaderPass

– SavePass

– MaskPass

• Pre-made passes– BloomPass

– FilmPass

– Etc.

var renderModel = new THREE.RenderPass(

scene,

camera

);

renderModel.clear = false;

composer.addPass(renderModel);

var effectBloom = new THREE.BloomPass(

0.8, // Strengh

25, // Kernel size

4, // Sigma

256 // Resolution

);

composer.addPass(effectBloom);

var hexvignette: {uniforms: {

tDiffuse: { type: "t", value: 0, texture: null },tHex: { type: "t", value: 1, texture: null},size: { type: "f", value: 512.0}, color: { type: "c", value: new THREE.Color(0x458ab1) }

},fragmentShader: [

"uniform float size;","uniform vec3 color;","uniform sampler2D tDiffuse;","uniform sampler2D tHex;",

"varying vec2 vUv;",

"void main() { ... }"

].join("\n")};

var effectHex = new THREE.ShaderPass(hexvignette);

effectHex.uniforms['size'].value = 512.0;

effectHex.uniforms['tHex'].texture = hexTexture;

composer.addPass(effectHex);

//…

effectHex.renderToScreen = true;

top related