2
0
Эх сурвалжийг харах

added webgl2 support, supported ES3 shader compilation (textureLod etc.), pbr sample working in webgl2

ncannasse 7 жил өмнө
parent
commit
21e7cef3ce
4 өөрчлөгдсөн 145 нэмэгдсэн , 103 устгасан
  1. 78 56
      h3d/impl/GlDriver.hx
  2. 2 0
      h3d/impl/MacroHelper.hx
  3. 32 14
      hxsl/GlslOut.hx
  4. 33 33
      samples/Pbr.hx

+ 78 - 56
h3d/impl/GlDriver.hx

@@ -11,6 +11,17 @@ import js.html.Uint16Array;
 import js.html.Uint8Array;
 import js.html.Float32Array;
 private typedef GL = js.html.webgl.GL;
+private extern class GL2 extends js.html.webgl.GL {
+	// webgl2
+	function drawBuffers( buffers : Array<Int> ) : Void;
+	static inline var RGBA16F = 0x881A;
+	static inline var RGBA32F = 0x8814;
+	static inline var ALPHA16F = 0x881C;
+	static inline var ALPHA32F = 0x8816;
+	static inline var RGBA8	   = 0x8058;
+	static inline var BGRA 		 = 0x80E1;
+	static inline var HALF_FLOAT = 0x140B;
+}
 private typedef Uniform = js.html.webgl.UniformLocation;
 private typedef Program = js.html.webgl.Program;
 private typedef GLShader = js.html.webgl.Shader;
@@ -107,7 +118,7 @@ class GlDriver extends Driver {
 	var canvas : js.html.CanvasElement;
 	var mrtExt : { function drawBuffersWEBGL( colors : Array<Int> ) : Void; };
 	static var UID = 0;
-	public var gl : js.html.webgl.RenderingContext;
+	public var gl : GL2;
 	#end
 
 	#if (hlsdl||usegl)
@@ -137,17 +148,25 @@ class GlDriver extends Driver {
 	var curTargetMip : Int;
 
 	var debug : Bool;
+	var glDebug : Bool;
 	var boundTextures : Array<Texture> = [];
+	var glES : Null<Float>;
 	var shaderVersion : Null<Int>;
 	var firstShader = true;
 
 	public function new(antiAlias=0) {
 		#if js
 		canvas = @:privateAccess hxd.Stage.getInstance().canvas;
-		gl = canvas.getContextWebGL({alpha:false,antialias:antiAlias>0});
+		var options = {alpha:false,antialias:antiAlias>0};
+		gl = cast canvas.getContext("webgl2",options);
+		if( gl == null )
+			gl = cast canvas.getContextWebGL(options);
 		if( gl == null ) throw "Could not acquire GL context";
 		// debug if webgl_debug.js is included
-		untyped if( __js__('typeof')(WebGLDebugUtils) != "undefined" ) gl = untyped WebGLDebugUtils.makeDebugContext(gl);
+		untyped if( __js__('typeof')(WebGLDebugUtils) != "undefined" ) {
+			gl = untyped WebGLDebugUtils.makeDebugContext(gl);
+			glDebug = true;
+		}
 		#if multidriver
 		canvas.setAttribute("class", canvas.getAttribute("class") + " _id_" + (UID++));
 		#end
@@ -157,19 +176,26 @@ class GlDriver extends Driver {
 		curAttribs = 0;
 		curMatBits = -1;
 		defStencil = new Stencil();
-		#if (hlsdl || usegl)
+
 		var v : String = gl.getParameter(GL.VERSION);
-		if( v.indexOf("ES") < 0 ){
+		var reg = ~/ES ([0-9]+\.[0-9]+)/;
+		if( reg.match(v) )
+			glES = Std.parseFloat(reg.matched(1));
+
+		#if !js
+		if( glES == null ) {
 			commonVA = gl.createVertexArray();
 			gl.bindVertexArray( commonVA );
 		}
-
+		#end
 
 		var reg = ~/[0-9]+\.[0-9]+/;
 		var v : String = gl.getParameter(GL.SHADING_LANGUAGE_VERSION);
-		if( v.indexOf("ES") < 0 &&reg.match(v) )
-			shaderVersion = hxd.Math.imin( 150, Math.round( Std.parseFloat(reg.matched(0)) * 100 ) );
+		if( reg.match(v) )
+			shaderVersion = Math.round( Std.parseFloat(reg.matched(0)) * 100 );
 
+		#if !js
+		gl.enable(GL.TEXTURE_CUBE_MAP_SEAMLESS);
 		gl.pixelStorei(GL.PACK_ALIGNMENT, 1);
 		gl.pixelStorei(GL.UNPACK_ALIGNMENT, 1);
 		gl.finish(); // prevent glError() on first bufferData
@@ -256,26 +282,14 @@ class GlDriver extends Driver {
 		if( p == null ) {
 			p = new CompiledProgram();
 			var glout = new ShaderCompiler();
-			if( shaderVersion != null )
-				glout.version = shaderVersion;
-			else
-				glout.glES = true;
-
+			glout.glES = glES;
+			glout.version = shaderVersion;
 			p.vertex = compileShader(glout,shader.vertex);
 			p.fragment = compileShader(glout,shader.fragment);
 
-			#if shader_debug_dump
-			hxsl.UniqueChecker.get().add(shader, function(s) {
-				var out = new ShaderCompiler();
-				out.version = glout.version;
-				out.glES = glout.glES;
-				return out.run(s);
-			});
-			#end
-
 			p.p = gl.createProgram();
 			#if (hlsdl || usegl)
-			if( !glout.glES ) {
+			if( glES == null ) {
 				var outCount = 0;
 				for( v in shader.fragment.data.vars )
 					switch( v.kind ) {
@@ -621,11 +635,9 @@ class GlDriver extends Driver {
 
 	function getChannels( t : Texture ) {
 		return switch( t.internalFmt ) {
-		#if !js
-		case GL.RGBA32F, GL.RGBA16F: GL.RGBA;
-		case GL.ALPHA16F, GL.ALPHA32F: GL.ALPHA;
-		case GL.RGBA8: GL.BGRA;
-		#end
+		case GL2.RGBA32F, GL2.RGBA16F: GL.RGBA;
+		case GL2.ALPHA16F, GL2.ALPHA32F: GL.ALPHA;
+		case GL2.RGBA8: GL2.BGRA;
 		case GL.RGBA: GL.RGBA;
 		case GL.ALPHA: GL.ALPHA;
 		default: throw "Invalid format " + t.internalFmt;
@@ -635,10 +647,7 @@ class GlDriver extends Driver {
 	override function isSupportedFormat( fmt : h3d.mat.Data.TextureFormat ) {
 		return switch( fmt ) {
 		case RGBA, ALPHA8: true;
-		case RGBA32F: hasFeature(FloatTextures);
-		#if !js
-		case ALPHA16F, ALPHA32F, RGBA16F: hasFeature(FloatTextures);
-		#end
+		case RGBA16F, RGBA32F, ALPHA16F, ALPHA32F: hasFeature(FloatTextures);
 		default: false;
 		}
 	}
@@ -653,25 +662,19 @@ class GlDriver extends Driver {
 		case ALPHA8:
 			tt.internalFmt = GL.ALPHA;
 		case RGBA32F if( hasFeature(FloatTextures) ):
-			#if js
-			tt.pixelFmt = GL.FLOAT;
-			#else
-			tt.internalFmt = GL.RGBA32F;
+			tt.internalFmt = GL2.RGBA32F;
 			tt.pixelFmt = GL.FLOAT;
-			#end
-		#if !js
-		case BGRA:
-			tt.internalFmt = GL.RGBA8;
 		case RGBA16F if( hasFeature(FloatTextures) ):
-			tt.pixelFmt = GL.HALF_FLOAT;
-			tt.internalFmt = GL.RGBA16F;
+			tt.pixelFmt = GL2.HALF_FLOAT;
+			tt.internalFmt = GL2.RGBA16F;
 		case ALPHA16F if( hasFeature(FloatTextures) ):
-			tt.pixelFmt = GL.HALF_FLOAT;
-			tt.internalFmt = GL.ALPHA16F;
+			tt.pixelFmt = GL2.HALF_FLOAT;
+			tt.internalFmt = GL2.ALPHA16F;
 		case ALPHA32F if( hasFeature(FloatTextures) ):
 			tt.pixelFmt = GL.FLOAT;
-			tt.internalFmt = GL.ALPHA32F;
-		#end
+			tt.internalFmt = GL2.ALPHA32F;
+		case BGRA:
+			tt.internalFmt = GL2.RGBA8;
 		default:
 			throw "Unsupported texture format "+t.format;
 		}
@@ -1069,7 +1072,9 @@ class GlDriver extends Driver {
 
 	function setDrawBuffers( k : Int ) {
 		#if js
-		if( mrtExt != null )
+		if( glES >= 3 )
+			gl.drawBuffers(CBUFFERS[k]);
+		else if( mrtExt != null )
 			mrtExt.drawBuffersWEBGL(CBUFFERS[k]);
 		#elseif (hlsdl || usegl)
 		gl.drawBuffers(k, CBUFFERS);
@@ -1113,9 +1118,8 @@ class GlDriver extends Driver {
 		if( tex.depthBuffer != null && (tex.depthBuffer.width != tex.width || tex.depthBuffer.height != tex.height) )
 			throw "Invalid depth buffer size : does not match render target size";
 
-		#if js
-		if( mipLevel > 0 ) throw "Cannot render to mipLevel, use upload() instead";
-		#end
+		if( glES == 1 && mipLevel > 0 ) throw "Cannot render to mipLevel in WebGL1, use upload() instead";
+
 		if( tex.t == null )
 			tex.alloc();
 
@@ -1143,6 +1147,14 @@ class GlDriver extends Driver {
 		gl.viewport(0, 0, tex.width >> mipLevel, tex.height >> mipLevel);
 		for( i in 0...boundTextures.length )
 			boundTextures[i] = null;
+
+		#if js
+		if( glDebug ) {
+			var code = gl.checkFramebufferStatus(GL.FRAMEBUFFER);
+			if( code != GL.FRAMEBUFFER_COMPLETE )
+				throw "Invalid frame buffer: "+code;
+		}
+		#end
 	}
 
 	override function setRenderTargets( textures : Array<h3d.mat.Texture> ) {
@@ -1187,21 +1199,31 @@ class GlDriver extends Driver {
 	override function hasFeature( f : Feature ) : Bool {
 		return switch( f ) {
 		#if hl
+
 		case StandardDerivatives, FloatTextures, MultipleRenderTargets, Queries:
-			true; // runtime extension detect required ?
+			true;
+
 		#else
+
+		case StandardDerivatives, MultipleRenderTargets if( glES >= 3 ):
+			true;
+
+		case FloatTextures if( glES >= 3 ):
+			gl.getExtension('EXT_color_buffer_float') != null; // allow render to 16f/32f textures (not standard in webgl 2)
+
 		case StandardDerivatives:
 			gl.getExtension('OES_standard_derivatives') != null;
+
 		case FloatTextures:
-			gl.getExtension('OES_texture_float') != null && gl.getExtension('OES_texture_float_linear') != null;
+			gl.getExtension('OES_texture_float') != null && gl.getExtension('OES_texture_float_linear') != null &&
+			gl.getExtension('OES_texture_half_float') != null && gl.getExtension('OES_texture_half_float_linear') != null;
+
 		case MultipleRenderTargets:
-			#if js
 			mrtExt != null || (mrtExt = gl.getExtension('WEBGL_draw_buffers')) != null;
-			#else
-			false; // no support for glDrawBuffers in OpenFL
-			#end
+
 		case Queries:
 			false;
+
 		#end
 		case HardwareAccelerated:
 			true;

+ 2 - 0
h3d/impl/MacroHelper.hx

@@ -10,6 +10,8 @@ class MacroHelper {
 		switch( e.expr ) {
 		case EConst(CIdent("gl")):
 			e.expr = EConst(CIdent("GL"));
+		case EConst(CIdent("GL2")):
+			e.expr = EConst(CIdent("GL"));
 		default:
 			haxe.macro.ExprTools.iter(e, replaceGLLoop);
 		}

+ 32 - 14
hxsl/GlslOut.hx

@@ -24,6 +24,7 @@ class GlslOut {
 		m.set(ToFloat, "float");
 		m.set(ToBool, "bool");
 		m.set(Texture2D, "_texture2D");
+		m.set(Texture2DLod, "_texture2DLod");
 		m.set(LReflect, "reflect");
 		m.set(Mat3x4, "_mat3x4");
 		for( g in m )
@@ -41,9 +42,11 @@ class GlslOut {
 	var allNames : Map<String, Int>;
 	var outIndexes : Map<Int, Int>;
 	var intelDriverFix : Bool;
+	var isES(get,never) : Bool;
+	var isES2(get,never) : Bool;
 	public var varNames : Map<Int,String>;
 	public var flipY : Bool;
-	public var glES : Bool;
+	public var glES : Null<Float>;
 	public var version : Null<Int>;
 
 	public function new() {
@@ -52,6 +55,9 @@ class GlslOut {
 		flipY = true;
 	}
 
+	inline function get_isES() return glES != null;
+	inline function get_isES2() return glES != null && glES <= 2;
+
 	inline function add( v : Dynamic ) {
 		buf.add(v);
 	}
@@ -224,17 +230,28 @@ class GlslOut {
 			case Texture2D:
 				// convert S/T (bottom left) to U/V (top left)
 				// we don't use 1. because of pixel rounding (fixes artifacts in blur)
-				decl('vec4 _texture2D( sampler2D t, vec2 v ) { return ${glES?"texture2D":"texture"}(t,vec2(v.x,${flipY?"0.999999-v.y":"v.y"})); }');
+				decl('vec4 _texture2D( sampler2D t, vec2 v ) { return ${isES2?"texture2D":"texture"}(t,${flipY?'vec2(v.x,0.999999-v.y)':'v'}); }');
 			case TextureCube:
-				if( !glES ) {
+				if( !isES2 ) {
 					add("texture");
 					return;
 				}
+			case Texture2DLod:
+				var tlod = "texture2DLod";
+				if( isES2 ) {
+					decl("#extension GL_EXT_shader_texture_lod : enable");
+					tlod = "texture2DLodEXT";
+				}
+				decl('vec4 _texture2DLod( sampler2D t, vec2 v, float lod ) { return $tlod(t,${flipY?'vec2(v.x,0.999999-v.y)':'v'}); }');
 			case TextureCubeLod:
-				if( !glES ) {
+				if( !isES2 ) {
 					add("textureLod");
 					return;
 				}
+				if( version <= 100 ) {
+					decl("#extension GL_EXT_shader_texture_lod : enable");
+					decl("vec4 textureCubeLod( samplerCube t, vec3 coord, float lod ) { return textureCubeLodEXT(t,coord,lod); }");
+				}
 			default:
 			}
 			add(GLOBALS.get(g));
@@ -456,7 +473,7 @@ class GlslOut {
 		if( v.kind == Output ) {
 			if( isVertex )
 				return "gl_Position";
-			if( glES ) {
+			if( isES2 ) {
 				if( outIndexes == null )
 					return "gl_FragColor";
 				return 'gl_FragData[${outIndexes.get(v.id)}]';
@@ -530,11 +547,11 @@ class GlslOut {
 			case Param, Global:
 				add("uniform ");
 			case Input:
-				add( glES ? "attribute " : "in ");
+				add( isES2 ? "attribute " : "in ");
 			case Var:
-				add( glES ? "varying " : (isVertex ? "out " : "in "));
+				add( isES2 ? "varying " : (isVertex ? "out " : "in "));
 			case Output:
-				if( glES ) {
+				if( isES2 ) {
 					outIndexes.set(v.id, outIndex++);
 					continue;
 				}
@@ -562,7 +579,7 @@ class GlslOut {
 
 		if( outIndex < 2 )
 			outIndexes = null;
-		else if( !isVertex && glES )
+		else if( !isVertex && isES2 )
 			decl("#extension GL_EXT_draw_buffers : enable");
 
 		var tmp = buf;
@@ -595,10 +612,10 @@ class GlslOut {
 			add("\n\n");
 		}
 
-		if( version != null )
-			decl("#version " + version);
-		else if( glES )
-			decl("#version 100");
+		if( isES )
+			decl("#version "+version+(version > 150 ? " es" : ""))
+		else if( version != null )
+			decl("#version " + (version > 150 ? 150 : version));
 		else
 			decl("#version 130"); // OpenGL 3.0
 
@@ -610,7 +627,8 @@ class GlslOut {
 	public static function compile( s : ShaderData ) {
 		var out = new GlslOut();
 		#if js
-		out.glES = true;
+		out.glES = 1;
+		out.version = 100;
 		#end
 		return out.run(s);
 	}

+ 33 - 33
samples/Pbr.hx

@@ -266,11 +266,9 @@ class IrradBase extends h3d.shader.ScreenShader {
 	static var SRC = {
 
 		@const var samplesBits : Int;
-		#if (js || flash)
-		@const var SamplesCount : Int;
+		@const var useTable : Bool;
+		@const(1024) var SamplesCount : Int;
 		@param var hammerTbl : Array<Vec4,SamplesCount>;
-		#end
-
 		function _reversebits( i : Int ) : Int {
 			var r = (i << 16) | (i >>> 16);
 			r = ((r & 0x00ff00ff) << 8) | ((r & 0xff00ff00) >>> 8);
@@ -281,12 +279,10 @@ class IrradBase extends h3d.shader.ScreenShader {
 		}
 
 		function hammersley( i : Int, max : Int ) : Vec2 {
-			#if (js || flash)
-			return hammerTbl[i].xy;
-			#else
+			if( useTable )
+				return hammerTbl[i].xy;
 			var ri = _reversebits(i) * 2.3283064365386963e-10;
 			return vec2(float(i) / float(max), ri);
-			#end
 		}
 
 		function importanceSampleGGX( roughness : Float, p : Vec2, n : Vec3 ) : Vec3 {
@@ -359,8 +355,7 @@ class Irradiance extends IrradBase {
 			var n = getNormal();
 			var totalWeight = 1e-10;
 			var numSamples = 1 << samplesBits;
-			#if (js || flash) @:unroll #end
-			for( i in 0...1 << samplesBits ) {
+			for( i in 0...numSamples ) {
 				var p = hammersley(i, numSamples);
 				var l : Vec3;
 				if( isSpecular ) {
@@ -403,8 +398,7 @@ class IrradianceLut extends IrradBase {
 			var n = vec3(0, 0, 1.);
 			var numSamples = 1 << samplesBits;
 			var a = 0., b = 0.;
-			#if (js || flash) @:unroll #end
-			for( i in 0...1 << samplesBits ) {
+			for( i in 0...numSamples ) {
 				var xi = hammersley(i, numSamples);
 				var h = importanceSampleGGX( roughness, xi, n );
 				var l = reflect(-v, h);
@@ -430,6 +424,26 @@ class IrradianceLut extends IrradBase {
 
 }
 
+class CubeMap extends hxsl.Shader {
+
+	static var SRC = {
+
+		var pixelColor : Vec4;
+		var transformedNormal : Vec3;
+		@param var texture : SamplerCube;
+		@param var lod : Float;
+		function fragment() {
+			pixelColor.rgb *= textureCubeLod(texture,transformedNormal,lod).rgb;
+		}
+
+	}
+
+	public function new(texture) {
+		super();
+		this.texture = texture;
+	}
+}
+
 class Pbr extends hxd.App {
 
 	var fui : h2d.Flow;
@@ -457,7 +471,7 @@ class Pbr extends hxd.App {
 
 		font = hxd.res.DefaultFont.get();
 
-		#if (js || flash)
+		#if flash
 		new h2d.Text(font, s2d).text = "Not supported on this platform (requires render to mipmap target and fragment textureCubeLod support)";
 		return;
 		#end
@@ -522,15 +536,9 @@ class Pbr extends hxd.App {
 		axis.visible = false;
 
 		var size, ssize;
-		#if (js || flash)
-		sampleBits = 5;
-		size = 16;
-		ssize = 16;
-		#else
 		size = 64;
 		ssize = 256;
 		sampleBits = 10;
-		#end
 
 		computeIrradLut();
 
@@ -545,7 +553,7 @@ class Pbr extends hxd.App {
 		shader.irrDiffuse = irradDiffuse;
 		shader.irrSpecular = irradSpecular;
 
-		var cubeShader = bg.material.mainPass.addShader(new h3d.shader.CubeMap(envMap));
+		var cubeShader = bg.material.mainPass.addShader(new CubeMap(envMap));
 
 		shader.defMetalness = 0.2;
 		shader.defRoughness = 0.5;
@@ -615,7 +623,6 @@ class Pbr extends hxd.App {
 			sphere.visible = !sphere.visible;
 		});
 
-		addChoice("EnvMap", ["Default", "IDiff", "ISpec"], function(i) cubeShader.texture = [envMap, irradDiffuse, irradSpecular][i]);
 		var r = Math.sqrt(2);
 		var cube = new h3d.prim.Cube(r,r,r);
 		cube.unindex();
@@ -624,6 +631,10 @@ class Pbr extends hxd.App {
 		cube.translate( -r * 0.5, -r * 0.5, -r * 0.5);
 		var prims : Array<h3d.prim.Primitive> = [sp, cube];
 		addChoice("Prim", ["Sphere","Cube"], function(i) sphere.primitive = prims[i], prims.indexOf(sphere.primitive));
+
+		addChoice("EnvMap", ["Default", "IDiff", "ISpec"], function(i) cubeShader.texture = [envMap, irradDiffuse, irradSpecular][i]);
+		addSlider("EnvLod", 0, 10, function() return cubeShader.lod, function(v) cubeShader.lod = v);
+
 	}
 
 	function addSeparator() {
@@ -648,15 +659,10 @@ class Pbr extends hxd.App {
 	}
 
 	function computeIrradLut() {
-		var irradLut = new h3d.mat.Texture(128, 128, [Target], #if js RGBA32F #else RGBA16F #end);
+		var irradLut = new h3d.mat.Texture(128, 128, [Target], RGBA16F);
 		irradLut.name = "irradLut";
 		var screen = new h3d.pass.ScreenFx(new IrradianceLut());
 		screen.shader.samplesBits = sampleBits;
-		#if (js || flash)
-		var nsamples = 1 << sampleBits;
-		screen.shader.SamplesCount = nsamples;
-		screen.shader.hammerTbl = [for( i in 0...nsamples ) hammersley(i, nsamples)];
-		#end
 
 		engine.driver.setRenderTarget(irradLut);
 		screen.render();
@@ -670,12 +676,6 @@ class Pbr extends hxd.App {
 		screen.shader.samplesBits = sampleBits;
 		screen.shader.envMap = envMap;
 
-		#if (js || flash)
-		var nsamples = 1 << sampleBits;
-		screen.shader.SamplesCount = nsamples;
-		screen.shader.hammerTbl = [for( i in 0...nsamples ) hammersley(i, nsamples)];
-		#end
-
 		for( i in 0...6 ) {
 			screen.shader.face = i;
 			engine.driver.setRenderTarget(irradDiffuse, i);