浏览代码

Add anisotropy and pbrSppecularColor

ShiroSmith 4 年之前
父节点
当前提交
bc493d50db
共有 4 个文件被更改,包括 382 次插入0 次删除
  1. 115 0
      hrt/prefab/pbr/Anisotropy.hx
  2. 91 0
      hrt/prefab/pbr/SpecularColor.hx
  3. 159 0
      hrt/shader/AnisotropicFoward.hx
  4. 17 0
      hrt/shader/SpecularColor.hx

+ 115 - 0
hrt/prefab/pbr/Anisotropy.hx

@@ -0,0 +1,115 @@
+package hrt.prefab.pbr;
+
+import hrt.shader.AnisotropicFoward.FlatValue;
+import hrt.shader.AnisotropicFoward.NoiseTexture;
+import hrt.shader.AnisotropicFoward;
+
+class Anisotropy extends Prefab {
+
+	public var amount : Float = 0.0;
+	public var direction : Float = 0.0;
+	public var noiseTexturePath : String = null;
+
+	public function new(?parent) {
+		super(parent);
+		type = "anisotropy";
+	}
+
+	override function load( obj : Dynamic ) {
+		super.load(obj);
+		if( obj.amount != null ) amount = obj.amount;
+		if( obj.direction != null ) direction = obj.direction;
+		if( obj.noiseTexturePath != null ) noiseTexturePath = obj.direcnoiseTexturePathtion;
+	}
+
+	override function save() {
+		var obj : Dynamic = super.save();
+		obj.amount = amount;
+		obj.direction = direction;
+		obj.noiseTexturePath = noiseTexturePath;
+		return obj;
+	}
+
+	override function makeInstance( ctx : Context ):Context {
+		ctx = ctx.clone(this);
+		refreshShaders(ctx);
+		updateInstance(ctx);
+		return ctx;
+	}
+
+	function refreshShaders( ctx : Context ) {
+		var af = new FlatValue();
+		var as = new AnisotropicFoward();
+		as.setPriority(-1);
+		var an = new NoiseTexture();
+		var noiseTexture = noiseTexturePath != null ? ctx.loadTexture(noiseTexturePath) : null;
+		var o = ctx.local3d;
+		for( m in o.getMaterials() ) {
+			m.mainPass.removeShader(m.mainPass.getShader(NoiseTexture));
+			m.mainPass.removeShader(m.mainPass.getShader(FlatValue));
+			m.mainPass.removeShader(m.mainPass.getShader(AnisotropicFoward));
+		}
+		for( m in o.getMaterials() ) {
+
+			if( m.mainPass.name != "forward" )
+				continue;
+
+			if( noiseTexture != null ) {
+				if( m.mainPass.getShader(NoiseTexture) == null )
+					m.mainPass.addShader(an);
+			}
+			else if( m.mainPass.getShader(FlatValue) == null ) {
+				m.mainPass.addShader(af);
+			}
+
+			if( m.mainPass.getShader(AnisotropicFoward) == null ) {
+				m.mainPass.addShader(as);
+			}
+		}
+	}
+
+	override function updateInstance( ctx : Context, ?propName : String ) {
+		for( m in ctx.local3d.getMaterials() ) {
+			var af = m.mainPass.getShader(FlatValue);
+			var an = m.mainPass.getShader(NoiseTexture);
+			if( af != null ) {
+				af.amount = amount;
+				var angle = hxd.Math.degToRad(direction);
+				af.dirVector.set(hxd.Math.cos(angle), hxd.Math.sin(angle), 0);
+			}
+			if( an != null ) {
+				an.noiseTexture = noiseTexturePath != null ? ctx.loadTexture(noiseTexturePath) : null;
+			}
+		}
+	}
+
+	#if editor
+	override function getHideProps() : HideProps {
+		return { 	icon : "cube", 
+					name : "Anisotropy",
+					allowParent : function(p) return p.to(Material) != null  };
+	}
+
+	override function edit( ctx : EditContext ) {
+		super.edit(ctx);
+
+		var props = new hide.Element('
+			<div class="group" name="Anisotropy">
+				<dl>
+					<dt>Amount</dt><dd><input type="range" min="0" max="1" field="amount"/></dd>
+					<dt>Direction</dt><dd><input type="range" min="0" max="180" field="direction"/></dd>
+					<dt>Noise Texture</dt><dd><input type="texturepath" field="noiseTexturePath"/>
+				</dl>
+			</div>
+		');
+
+		ctx.properties.add(props, this, function(pname) {
+			if( pname == "noiseTexturePath" )
+				refreshShaders(ctx.getContext(this));
+			ctx.onChange(this, pname);
+		});
+	}
+	#end
+
+	static var _ = Library.register("anisotropy", Anisotropy);
+}

+ 91 - 0
hrt/prefab/pbr/SpecularColor.hx

@@ -0,0 +1,91 @@
+package hrt.prefab.pbr;
+
+class SpecularColor extends Prefab {
+
+	// Amount of dielectric specular reflection. Specifies facing (along normal) reflectivity in the most common 0 - 8% range.
+	public var specular : Float = 0.5;
+
+	// Tints the facing specular reflection using the base color, while glancing reflection remains white.
+	// Normal dielectrics have colorless reflection, so this parameter is not technically physically correct 
+	// and is provided for faking the appearance of materials with complex surface structure.
+	public var specularTint : Float = 0.0;
+
+	public function new(?parent) {
+		super(parent);
+		type = "specularColor";
+	}
+
+	override function load( obj : Dynamic ) {
+		super.load(obj);
+		if( obj.specularTint != null ) specularTint = obj.specularTint;
+		if( obj.specular != null ) specular = obj.specular;
+	}
+
+	override function save() {
+		var obj : Dynamic = super.save();
+		obj.specularTint = specularTint;
+		obj.specular = specular;
+		return obj;
+	}
+
+	override function makeInstance( ctx : Context ):Context {
+		ctx = ctx.clone(this);
+		refreshShaders(ctx);
+		updateInstance(ctx);
+		return ctx;
+	}
+
+	override function updateInstance( ctx : Context, ?propName : String ) {
+		for( m in ctx.local3d.getMaterials() ) {
+			var sc = m.mainPass.getShader(hrt.shader.SpecularColor);
+			if( sc != null ) {
+				sc.specular = specular;
+				sc.specularTint = specularTint;
+			}
+		}
+	}
+
+	function refreshShaders( ctx : Context ) {
+		var sc = new hrt.shader.SpecularColor();
+		var o = ctx.local3d;
+		for( m in o.getMaterials() ) {
+			m.mainPass.removeShader(m.mainPass.getShader(hrt.shader.SpecularColor));
+		}
+		for( m in o.getMaterials() ) {
+
+			if( m.mainPass.name != "forward" )
+				continue;
+
+			if( m.mainPass.getShader(hrt.shader.SpecularColor) == null ) {
+				m.mainPass.addShader(sc);
+			}
+		}
+	}
+
+	#if editor
+	override function getHideProps() : HideProps {
+		return { 	icon : "cube", 
+					name : "SpecularColor",
+					allowParent : function(p) return p.to(Material) != null };
+	}
+
+	override function edit( ctx : EditContext ) {
+		super.edit(ctx);
+
+		var props = new hide.Element('
+			<div class="group" name="Specular Color">
+				<dl>
+					<dt>Tint</dt><dd><input type="range" min="0" max="1" field="specularTint"/></dd>
+					<dt>Amount</dt><dd><input type="range" min="0" max="1" field="specular"/>
+				</dl>
+			</div>
+		');
+
+		ctx.properties.add(props, this, function(pname) {
+			ctx.onChange(this, pname);
+		});
+	}
+	#end
+
+	static var _ = Library.register("specularColor", SpecularColor);
+}

+ 159 - 0
hrt/shader/AnisotropicFoward.hx

@@ -0,0 +1,159 @@
+package hrt.shader;
+
+class FlatValue extends hxsl.Shader {
+	static var SRC = {
+
+		@param var amount : Float;
+		@param var dirVector : Vec3;
+
+		var anisotropy : Float;
+		var direction : Vec3;
+
+		function fragment()  {
+			anisotropy = amount;
+			direction = dirVector;
+		}
+	}
+}
+
+class NoiseTexture extends hxsl.Shader {
+	static var SRC = {
+
+		@param var noiseTexture : Sampler2D;
+		var anisotropy : Float;
+		var direction : Vec3;
+		var calculatedUV : Vec2;
+
+		function fragment()  {
+			var noise = noiseTexture.get(calculatedUV).rg;
+			anisotropy = noise.r;
+			var angle = noise.g * 2 * PI;
+			direction = vec3(cos(angle), sin(angle), 0.0);
+		}
+	}
+}
+
+class AnisotropicFoward extends h3d.shader.pbr.DefaultFoward {
+	static var SRC = {
+
+		@global var global : {
+            @perObject var modelView : Mat4;
+		};
+
+		var anisotropy : Float;
+		var direction : Vec3;
+
+		var tangentWorld : Vec3;
+		var bitangentWorld : Vec3;
+
+		function getAnisotropicRoughness( roughness : Float, anisotropy : Float ) : Vec2 {
+			// [Burley12] Offers more pleasant and intuitive results, but is slightly more expensive
+			//var da = sqrt(1 - 0.9 * anisotropy);
+			//var at = anisotropy / da;
+			//var ab = anisotropy * da;
+
+			// [Kulla17] Allows creation of sharp highlights
+			var at = max(roughness * (1.0 + anisotropy), 0.001);
+			var ab = max(roughness * (1.0 - anisotropy), 0.001);
+
+			return vec2(at, ab);
+		}
+
+		function sqr( f : Float ) : Float { return f * f; }
+		function cos_theta( w : Vec3 ) : Float { return w.z; }
+		function cos_2_theta( w : Vec3 ) : Float { return w.z * w.z; }
+		function sin_2_theta( w : Vec3 ) : Float { return max(0., 1. -cos_2_theta(w)); }
+		function sin_theta( w : Vec3 ) : Float { return sqrt(sin_2_theta(w)); }
+		function tan_theta( w : Vec3 ) : Float { return sin_theta(w) / cos_theta(w); }
+		function cos_phi( w : Vec3 ) : Float { return (sin_theta(w) == 0.) ? 1. : clamp(w.x / sin_theta(w), -1., 1.); }
+		function sin_phi( w : Vec3 ) : Float { return (sin_theta(w) == 0.) ? 0. : clamp(w.y / sin_theta(w), -1., 1.); }
+
+		function normalDistributionGGXAnisotropic( omega_h : Vec3, alpha_x : Float, alpha_y : Float ) : Float {
+			var slope_x = -(omega_h.x / omega_h.z);
+			var slope_y = -(omega_h.y / omega_h.z);
+			var denom = (1. + (sqr(slope_x) / sqr(alpha_x)) + (sqr(slope_y) / sqr(alpha_y)) );
+			return 1. / ( ( PI * alpha_x * alpha_y) * (sqr(denom)) ) / sqr(sqr(cos_theta(omega_h)));
+		}
+
+		function lambdaGGXAnisotropic( omega : Vec3, alpha_x : Float, alpha_y: Float) : Float {
+			var cos_phi = cos_phi(omega);
+			var sin_phi = sin_phi(omega);
+			var alpha_o = sqrt(sqr(cos_phi) * sqr(alpha_x) + sqr(sin_phi) * sqr(alpha_y));
+			var a = 1. / (alpha_o * tan_theta(omega));
+			return( 0.5 * (-1. + sqrt(1. + 1. / (a*a))) );
+		}
+
+		function fresnelSchlick( wo_dot_wh : Float, F0 : Vec3 ) : Vec3 {
+			return F0 + (1. - F0) * pow(1. - wo_dot_wh, 5.);
+		}
+
+		function directLighting( lightColor : Vec3, lightDirection : Vec3) : Vec3 {
+
+			var NdL = clamp(transformedNormal.dot(lightDirection), 0.0, 1.0);
+			var result = vec3(0,0,0);
+
+			if( lightColor.dot(lightColor) > 0.0001 && NdL > 0.0 ) {
+
+				var ar = getAnisotropicRoughness(roughness, anisotropy);
+				var at = ar.x;
+				var ab = ar.y;
+
+				// PBRT - Computations are done in tangent space
+				var TBN_t = mat3(tangentWorld, bitangentWorld, transformedNormal);
+				var wo = normalize(view * TBN_t);
+				var wi = normalize(lightDirection * TBN_t);
+				var wg = vec3(0,0,1); // normalize(transformedNormal * TBN_t);
+				var wh = normalize(wo + wi);
+				var wi_dot_wh = clamp(dot(wi, wh),0.,1.); 		// saturate(dot(L,H))
+				var wg_dot_wi = clamp(cos_theta(wi),0.,1.); 	// saturate(dot(N,L))
+
+				var alpha_x = at * at;
+        		var alpha_y = ab * ab;
+				var D = normalDistributionGGXAnisotropic(wh, alpha_x, alpha_y);
+				var F = fresnelSchlick(wi_dot_wh, F0);
+				var lambda_wo = lambdaGGXAnisotropic(wo, alpha_x, alpha_y);
+				var lambda_wi = lambdaGGXAnisotropic(wi, alpha_x, alpha_y);
+				var G = 1. / (1. + lambda_wo + lambda_wi);
+
+				var specular = (D * F * G) / ( 4. * cos_theta(wi) * cos_theta(wo) ) ;
+				var diffuse = albedo / PI * (1.0 - metalness);
+
+				result = (diffuse + specular) * lightColor * wg_dot_wi;
+			}
+
+			return result;
+		}
+
+		function indirectLighting() : Vec3 {
+
+			var anisotropicDirection = anisotropy >= 0.0 ? bitangentWorld : tangentWorld;
+			var anisotropicTangent = cross(anisotropicDirection, view);
+			var anisotropicNormal = cross(anisotropicTangent, anisotropicDirection);
+			var bentNormal = normalize(mix(transformedNormal, anisotropicNormal, anisotropy));
+			var rotatedNormal = rotateNormal(bentNormal);
+			var reflectVec = reflect(-view, bentNormal);
+			var rotatedReflecVec = rotateNormal(reflectVec);
+
+			var F = F0 + (max(vec3(1 - roughness), F0) - F0) * exp2( ( -5.55473 * NdV - 6.98316) * NdV );
+			var diffuse = irrDiffuse.get(rotatedNormal).rgb * albedo;
+			var envSpec = textureLod(irrSpecular, rotatedReflecVec, roughness * irrSpecularLevels).rgb;
+			var envBRDF = irrLut.get(vec2(roughness, NdV));
+			var specular = envSpec * (F * envBRDF.x + envBRDF.y);
+			var indirect = (diffuse * (1 - metalness) * (1 - F) + specular) * irrPower;
+
+			return indirect * occlusion;
+		}
+
+		function init() {
+			view = (cameraPosition - transformedPosition).normalize();
+			NdV = transformedNormal.dot(view).max(0.);
+			metalness = output.metalness;
+			roughness = output.roughness;
+			occlusion = output.occlusion;
+			emissive = output.emissive;
+			var tmp = cross(direction * global.modelView.mat3(), transformedNormal).normalize();
+			bitangentWorld = cross(tmp, transformedNormal).normalize();
+			tangentWorld = cross(bitangentWorld, transformedNormal).normalize();
+		}
+	}
+}

+ 17 - 0
hrt/shader/SpecularColor.hx

@@ -0,0 +1,17 @@
+package hrt.shader;
+
+class SpecularColor extends hxsl.Shader {
+	static var SRC = {
+
+		@param var specular : Float;
+		@param var specularTint : Float;
+
+		var pbrSpecularColor : Vec3;
+		var albedo : Vec3;
+		
+		function fragment() {
+			pbrSpecularColor = mix(vec3(1.0), albedo, specularTint) * vec3(specular * 0.08);
+		}
+
+	}
+}