瀏覽代碼

conditional attributes and light system in gl

Nicolas Cannasse 12 年之前
父節點
當前提交
56bea0cec4
共有 4 個文件被更改,包括 210 次插入46 次删除
  1. 10 1
      h3d/impl/Shader.hx
  2. 98 39
      h3d/impl/WebglDriver.hx
  3. 99 5
      h3d/mat/MeshMaterial.hx
  4. 3 1
      samples/basic/Test.hx

+ 10 - 1
h3d/impl/Shader.hx

@@ -14,13 +14,17 @@ enum ShaderType {
 	Mat4;
 	Tex2d;
 	TexCube;
+	Struct( field : String, t : ShaderType );
+	Index( index : Int, t : ShaderType );
 }
 
+typedef Uniform = { name : String, loc : js.html.webgl.UniformLocation, type : ShaderType, index : Int }
+
 class ShaderInstance {
 
 	public var program : js.html.webgl.Program;
 	public var attribs : Array<{ name : String, type : ShaderType, etype : Int, offset : Int, index : Int, size : Int }>;
-	public var uniforms : Array<{ name : String, loc : js.html.webgl.UniformLocation, type : ShaderType, index : Int }>;
+	public var uniforms : Array<Uniform>;
 	public var stride : Int;
 	public function new() {
 	}
@@ -33,6 +37,11 @@ class Shader implements Dynamic {
 	
 	public function new() {
 	}
+	
+	function getConstants( vertex : Bool ) {
+		return "";
+	}
+
 }
 
 #else

+ 98 - 39
h3d/impl/WebglDriver.hx

@@ -196,16 +196,23 @@ class WebglDriver extends Driver {
 		case Mat2: 4;
 		case Mat3: 9;
 		case Mat4: 16;
-		case Tex2d, TexCube: throw "Unexpected " + t;
+		case Tex2d, TexCube, Struct(_), Index(_): throw "Unexpected " + t;
 		}
 	}
 	
 	function buildShaderInstance( shader : Shader ) {
 		var cl = Type.getClass(shader);
-		function compileShader(name, type) {
+		function compileShader(type) {
+			var vertex = type == GL.VERTEX_SHADER;
+			var name = vertex ? "VERTEX" : "FRAGMENT";
 			var code = Reflect.field(cl, name);
 			if( code == null ) throw "Missing " + Type.getClassName(cl) + "." + name + " shader source";
-			code = StringTools.trim(code);
+			var cst = shader.getConstants(vertex);
+			code = StringTools.trim(cst + code);
+			// replace haxe-like #if/#else/#end by GLSL ones
+			code = ~/#if ([A-Za-z0-9_]+)/g.replace(code, "#if defined($1)");
+			code = ~/#elseif ([A-Za-z0-9_]+)/g.replace(code, "#elif defined($1)");
+			code = code.split("#end").join("#endif");
 			var s = gl.createShader(type);
 			gl.shaderSource(s, code);
 			gl.compileShader(s);
@@ -217,8 +224,8 @@ class WebglDriver extends Driver {
 			}
 			return s;
 		}
-		var vs = compileShader("VERTEX", GL.VERTEX_SHADER);
-		var fs = compileShader("FRAGMENT", GL.FRAGMENT_SHADER);
+		var vs = compileShader(GL.VERTEX_SHADER);
+		var fs = compileShader(GL.FRAGMENT_SHADER);
 		
 		var p = gl.createProgram();
 		gl.attachShader(p, vs);
@@ -240,8 +247,28 @@ class WebglDriver extends Driver {
 			amap.set(inf.name, { index : k, inf : inf });
 		}
 		
+		
+		var code = gl.getShaderSource(vs);
+
+		// remove (and save) all #define's
+		var rdef = ~/#define ([A-Za-z0-9_]+)/;
+		var defs = new Map();
+		while( rdef.match(code) ) {
+			defs.set(rdef.matched(1), true);
+			code = rdef.matchedLeft() + rdef.matchedRight();
+		}
+		
+		// remove parts of the codes that are undefined
+		var rif = ~/#if defined\(([A-Za-z0-9_]+)\)([^#]+)#endif/;
+		while( rif.match(code) ) {
+			if( defs.get(rif.matched(1)) )
+				code = rif.matchedLeft() + rif.matched(2) + rif.matchedRight();
+			else
+				code = rif.matchedLeft() + rif.matchedRight();
+		}
+		
+		// extract attributes from code (so we know the offset and stride)
 		var r = ~/attribute[ \t\r\n]+([A-Za-z0-9_]+)[ \t\r\n]+([A-Za-z0-9_]+)/;
-		var code : String = Reflect.field(cl, "VERTEX");
 		var offset = 0;
 		while( r.match(code) ) {
 			var aname = r.matched(2);
@@ -255,23 +282,42 @@ class WebglDriver extends Driver {
 		}
 		inst.stride = offset;
 		
+		// list uniforms needed by shader
 		var nuni = gl.getProgramParameter(p, GL.ACTIVE_UNIFORMS);
 		inst.uniforms = [];
-		var texIndex = 0;
+		var texIndex = -1;
+		var r_array = ~/\[([0-9]+)\]$/;
 		for( k in 0...nuni ) {
 			var inf = gl.getActiveUniform(p, k);
+			if( inf.name.substr(0, 6) == "webgl_" )
+				continue; // skip native uniforms
 			var t = decodeTypeInt(inf.type);
-			inst.uniforms.push( {
-				name : inf.name,
-				type : t,
-				loc : gl.getUniformLocation(p, inf.name),
-				index : texIndex,
-			});
 			switch( t ) {
 			case Tex2d, TexCube:
 				texIndex++;
 			default:
 			}
+			var name = inf.name;
+			while( true ) {
+				if( r_array.match(name) ) {
+					name = r_array.matchedLeft();
+					t = Index(Std.parseInt(r_array.matched(1)), t);
+					continue;
+				}
+				var c = name.lastIndexOf(".");
+				if( c > 0 ) {
+					var field = name.substr(c + 1);
+					name = name.substr(0, c);
+					t = Struct(field, t);
+				}
+				break;
+			}
+			inst.uniforms.push( {
+				name : name,
+				type : t,
+				loc : gl.getUniformLocation(p, inf.name),
+				index : texIndex,
+			});
 		}
 			
 		inst.program = p;
@@ -299,37 +345,50 @@ class WebglDriver extends Driver {
 		for( u in curShader.uniforms ) {
 			var val : Dynamic = Reflect.field(shader, u.name);
 			if( val == null ) throw "Missing shader value " + u.name;
-			switch( u.type ) {
-			case Mat4:
-				var m : Matrix = val;
-				gl.uniformMatrix4fv(u.loc, false, new js.html.Float32Array(m.getFloats()));
-			case Tex2d:
-				var t : h3d.mat.Texture = val;
-				gl.activeTexture(GL.TEXTURE0 + u.index);
-				gl.bindTexture(GL.TEXTURE_2D, t.t);
-				var flags = TFILTERS[Type.enumIndex(t.mipMap)][Type.enumIndex(t.filter)];
-				gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, flags[0]);
-				gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, flags[1]);
-				gl.uniform1i(u.loc, u.index);
-			case Float:
-				gl.uniform1f(u.loc, val);
-			case Vec2:
-				var v : h3d.Vector = val;
-				gl.uniform2f(u.loc, v.x, v.y);
-			case Vec3:
-				var v : h3d.Vector = val;
-				gl.uniform3f(u.loc, v.x, v.y, v.z);
-			case Vec4:
-				var v : h3d.Vector = val;
-				gl.uniform4f(u.loc, v.x, v.y, v.z, v.w);
-			default:
-				throw "Unsupported uniform " + u.type;
-			}
+			setUniform(val, u, u.type);
 		}
 		
 		return change;
 	}
 	
+	function setUniform( val : Dynamic, u : Shader.Uniform, t : Shader.ShaderType ) {
+		switch( t ) {
+		case Mat4:
+			var m : Matrix = val;
+			gl.uniformMatrix4fv(u.loc, false, new js.html.Float32Array(m.getFloats()));
+		case Tex2d:
+			var t : h3d.mat.Texture = val;
+			gl.activeTexture(GL.TEXTURE0 + u.index);
+			gl.bindTexture(GL.TEXTURE_2D, t.t);
+			var flags = TFILTERS[Type.enumIndex(t.mipMap)][Type.enumIndex(t.filter)];
+			gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, flags[0]);
+			gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, flags[1]);
+			gl.uniform1i(u.loc, u.index);
+		case Float:
+			gl.uniform1f(u.loc, val);
+		case Vec2:
+			var v : h3d.Vector = val;
+			gl.uniform2f(u.loc, v.x, v.y);
+		case Vec3:
+			var v : h3d.Vector = val;
+			gl.uniform3f(u.loc, v.x, v.y, v.z);
+		case Vec4:
+			var v : h3d.Vector = val;
+			gl.uniform4f(u.loc, v.x, v.y, v.z, v.w);
+		case Struct(field, t):
+			var v = Reflect.field(val, field);
+			if( v == null ) throw "Missing shader field " + field;
+			setUniform(v, u, t);
+		case Index(index, t):
+			var v = val[index];
+			if( v == null ) throw "Missing shader index " + index;
+			setUniform(v, u, t);
+		default:
+			throw "Unsupported uniform " + u.type;
+		}
+		
+	}
+	
 	override function selectBuffer( v : VertexBuffer ) {
 		var stride : Int = untyped v.stride;
 		if( stride < curShader.stride )

+ 99 - 5
h3d/mat/MeshMaterial.hx

@@ -89,10 +89,10 @@ private class MeshShader extends h3d.impl.Shader {
 			if( lightSystem != null ) {
 				// calculate normal
 				var n = input.normal;
-				if( mpos != null ) n *= mpos;
+				if( mpos != null ) n *= mpos; // WRONG : we should use m33 only
 				if( hasSkin ) {
 					n = n * input.weights.x * skinMatrixes[input.indexes.x * (255 * 3)] + n * input.weights.y * skinMatrixes[input.indexes.y * (255 * 3)] + n * input.weights.z * skinMatrixes[input.indexes.z * (255 * 3)];
-					if( mpos != null ) n = n * mposInv;
+					if( mpos != null ) n = n * mposInv; // should be the 3x3 part only
 				}
 				var col = lightSystem.ambient;
 				n = n.normalize();
@@ -151,10 +151,76 @@ private class MeshShader extends h3d.impl.Shader {
 	}
 #else
 
+	public var hasVertexColor : Bool;
+	public var hasVertexColorAdd : Bool;
+	public var lightSystem(default,set) : LightSystem;
+
+	var lights : {
+		ambient : h3d.Vector,
+		dirsDir : Array<h3d.Vector>,
+		dirsColor : Array<h3d.Vector>,
+		pointsPos : Array<h3d.Vector>,
+		pointsColor : Array<h3d.Vector>,
+		pointsAtt : Array<h3d.Vector>,
+	};
+	
+	function set_lightSystem(l) {
+		this.lightSystem = l;
+		lights = {
+			ambient : l.ambient,
+			dirsDir : [for( l in l.dirs ) l.dir],
+			dirsColor : [for( l in l.dirs ) l.color],
+			pointsPos : [for( p in l.points ) p.pos],
+			pointsColor : [for( p in l.points ) p.color],
+			pointsAtt : [for( p in l.points ) p.att],
+		};
+		return l;
+	}
+	
+	override function getConstants(vertex) {
+		var cst = [];
+		if( hasVertexColor ) cst.push("#define hasVertexColor");
+		if( hasVertexColorAdd ) cst.push("#define hasVertexColorAdd");
+		if( lightSystem != null ) {
+			cst.push("#define hasLightSystem");
+			cst.push("const int numDirLights = " + lightSystem.dirs.length+";");
+			cst.push("const int numPointLights = " + lightSystem.points.length+";");
+		}
+		if( hasVertexColorAdd || lightSystem != null ) cst.push("#define hasFragColor");
+		return cst.join("\n");
+	}
+
 	static var VERTEX = "
 	
 		attribute vec3 pos;
 		attribute vec2 uv;
+		#if hasLightSystem
+		
+		attribute vec3 normal;
+		
+		// we can't use Array of structures in GLSL
+		struct LightSystem {
+			vec3 ambient;
+			vec3 dirsDir[numDirLights];
+			vec3 dirsColor[numDirLights];
+			vec3 pointsPos[numPointLights];
+			vec3 pointsColor[numPointLights];
+			vec3 pointsAtt[numPointLights];
+		};
+		uniform LightSystem lights;
+		
+		#end
+		#if hasVertexColor
+		attribute vec3 color;
+		#end
+		#if hasVertexColorAdd
+		attribute vec3 colorAdd;
+		varying lowp vec3 acolor;
+		#end
+
+		#if hasFragColor
+		varying lowp vec3 tcolor;
+		#end
 
 		uniform mat4 mpos;
 		uniform mat4 mproj;
@@ -162,8 +228,25 @@ private class MeshShader extends h3d.impl.Shader {
 		varying lowp vec2 tuv;
 
 		void main(void) {
-			gl_Position = mproj * mpos * vec4(pos, 1.0);
+			vec4 tpos = mpos * vec4(pos,1.0);
+			gl_Position = mproj * tpos;
 			tuv = uv;
+			#if hasLightSystem
+			vec3 n = mat3(mpos) * normal;
+			n = normalize(n);
+			vec3 col = lights.ambient;
+			for(int i = 0; i < numDirLights; i++ )
+				col += lights.dirsColor[i] * max(dot(n,-lights.dirsDir[i]),0.);
+			for(int i = 0; i < numPointLights; i++ ) {
+				vec3 d = tpos.xyz - lights.pointsPos[i];
+				float dist2 = dot(d,d);
+				float dist = sqrt(dist2);
+				col += lights.pointsColor[i] * (max(dot(n,d),0.) / dot(lights.pointsAtt[i],vec3(dist,dist2,dist2*dist)));
+			}
+			tcolor = col;
+			#elseif hasVertexColor
+			tcolor = color;
+			#end
 		}
 
 	";
@@ -172,9 +255,20 @@ private class MeshShader extends h3d.impl.Shader {
 	
 		varying lowp vec2 tuv;
 		uniform sampler2D tex;
-	
+
+		#if hasFragColor
+		varying lowp vec3 tcolor;
+		#end
+
 		void main(void) {
-			gl_FragColor = texture2D(tex, tuv);
+			lowp vec4 c = texture2D(tex, tuv);
+			#if hasFragColor
+			c.rgb *= tcolor;
+			#end
+			#if hasVertexColorAdd
+			c.rgb += acolor;
+			#end
+			gl_FragColor = c;
 		}
 			
 	";

+ 3 - 1
samples/basic/Test.hx

@@ -18,7 +18,6 @@ class Test {
 	}
 	
 	function start() {
-		hxd.System.setLoop(update);
 		
 		var prim = new h3d.prim.Cube();
 		prim.translate( -0.5, -0.5, -0.5);
@@ -37,6 +36,9 @@ class Test {
 			dirs : [{ dir : new h3d.Vector(-0.3,-0.5,-1), color : new h3d.Vector(1,1,1) }],
 			points : [{ pos : new h3d.Vector(1.5,0,0), color : new h3d.Vector(3,0,0), att : new h3d.Vector(0,0,1) }],
 		};
+		
+		update();
+		hxd.System.setLoop(update);
 	}
 	
 	function update() {