浏览代码

Redone 18-ibl example. It's now using textures that are filtered with cmft.

Dario Manesku 9 年之前
父节点
当前提交
a079ee53ef

+ 101 - 54
examples/18-ibl/fs_ibl_mesh.sc

@@ -1,77 +1,124 @@
 $input v_view, v_normal
 
 /*
- * Copyright 2014 Dario Manesku. All rights reserved.
+ * Copyright 2014-2016 Dario Manesku. All rights reserved.
  * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
  */
 
 #include "../common/common.sh"
-
-uniform vec4 u_params;
-uniform mat4 u_mtx;
-uniform vec4 u_flags;
-uniform vec4 u_rgbDiff;
-uniform vec4 u_rgbSpec;
+#include "uniforms.sh"
 
 SAMPLERCUBE(s_texCube, 0);
 SAMPLERCUBE(s_texCubeIrr, 1);
 
-#define u_glossiness u_params.x
-#define u_exposure   u_params.y
-#define u_diffspec   u_params.z
-
-#define u_doDiffuse     u_flags.x
-#define u_doSpecular    u_flags.y
-#define u_doDiffuseIbl  u_flags.z
-#define u_doSpecularIbl u_flags.w
-
-vec3 fresnel(vec3 _cspec, float _dot)
+vec3 calcFresnel(vec3 _cspec, float _dot)
 {
-	return _cspec + (1.0 - _cspec) * pow(1.0 - _dot, 5);
+	return _cspec + (1.0 - _cspec)*pow(1.0 - _dot, 5.0);
 }
 
-void main()
+vec3 calcLambert(vec3 _cdiff, float _ndotl)
 {
-	vec3 light  = vec3(0.0, 0.0, -1.0);
-	vec3 clight = vec3(1.0, 1.0,  1.0);
-
-	vec3 v = v_view;
-	vec3 n = normalize(v_normal);
-	vec3 l = normalize(light);
-	vec3 h = normalize(v + l);
-
-	float ndotl = clamp(dot(n, l), 0.0, 1.0); //diff
-	float ndoth = clamp(dot(n, h), 0.0, 1.0); //spec
-	float vdoth = clamp(dot(v, h), 0.0, 1.0); //spec fresnel
-	float ndotv = clamp(dot(n, v), 0.0, 1.0); //env spec fresnel
-
-	vec3 r = 2.0*ndotv*n - v; // reflect(v, n);
-
-	vec3 cubeR = normalize(mul(u_mtx, vec4(r, 0.0)).xyz);
-	vec3 cubeN = normalize(mul(u_mtx, vec4(n, 0.0)).xyz);
-
-	float mipLevel = min((1.0 - u_glossiness)*11.0 + 1.0, 8.0);
-	vec3 cenv = textureCubeLod(s_texCube, cubeR, mipLevel).xyz;
-
-	vec3 kd = u_rgbDiff.xyz;
-	vec3 ks = u_rgbSpec.xyz;
-
-	vec3 cs = ks * u_diffspec;
-	vec3 cd = kd * (1.0 - cs);
+	return _cdiff*_ndotl;
+}
 
-	vec3 diff = cd;
-	float pwr = exp2(u_glossiness * 11.0 + 1.0);
-	vec3 spec = cs * pow(ndoth, pwr) * ( (pwr + 8.0)/8.0) * fresnel(cs, vdoth);
+vec3 calcBlinn(vec3 _cspec, float _ndoth, float _ndotl, float _specPwr)
+{
+	float norm = (_specPwr+8.0)*0.125;
+	float brdf = pow(_ndoth, _specPwr)*_ndotl*norm;
+	return _cspec*brdf;
+}
 
-	vec3 ambspec = fresnel(cs, ndotv) * cenv;
-	vec3 ambdiff = cd * textureCube(s_texCubeIrr, cubeN).xyz;
+float specPwr(float _gloss)
+{
+	return exp2(10.0*_gloss+2.0);
+}
 
-	vec3 lc = (   diff * u_doDiffuse    +    spec * u_doSpecular   ) * ndotl * clight;
-	vec3 ec = (ambdiff * u_doDiffuseIbl + ambspec * u_doSpecularIbl);
+// Ref: http://the-witness.net/news/2012/02/seamless-cube-map-filtering/
+vec3 fixCubeLookup(vec3 _v, float _lod, float _topLevelCubeSize)
+{
+	float ax = abs(_v.x);
+	float ay = abs(_v.y);
+	float az = abs(_v.z);
+	float vmax = max(max(ax, ay), az);
+	float scale = 1.0 - exp2(_lod) / _topLevelCubeSize;
+	if (ax != vmax) { _v.x *= scale; }
+	if (ay != vmax) { _v.y *= scale; }
+	if (az != vmax) { _v.z *= scale; }
+	return _v;
+}
 
-	vec3 color = lc + ec;
+void main()
+{
+	// Light.
+	vec3 ld     = normalize(u_lightDir);
+	vec3 clight = u_lightCol;
+
+	// Input.
+	vec3 nn = normalize(v_normal);
+	vec3 vv = normalize(v_view);
+	vec3 hh = normalize(vv + ld);
+
+	float ndotv = clamp(dot(nn, vv), 0.0, 1.0);
+	float ndotl = clamp(dot(nn, ld), 0.0, 1.0);
+	float ndoth = clamp(dot(nn, hh), 0.0, 1.0);
+	float hdotv = clamp(dot(hh, vv), 0.0, 1.0);
+
+	// Material params.
+	vec3  albedo       = u_rgbDiff.xyz;
+	float reflectivity = u_reflectivity;
+	float gloss        = u_glossiness;
+
+	// Reflection.
+	vec3 refl;
+	if (0.0 == u_metalOrSpec) // Metalness workflow.
+	{
+		refl = mix(vec3_splat(0.04), albedo, reflectivity);
+	}
+	else // Specular workflow.
+	{
+		refl = u_rgbSpec.xyz * vec3_splat(reflectivity);
+	}
+	vec3 dirF0 = calcFresnel(refl, hdotv);
+	vec3 envF0 = calcFresnel(refl, ndotv);
+
+	// Direct lighting.
+	vec3 dirSpec = dirF0;
+	vec3 dirDiff = albedo * 1.0-dirF0;
+
+	vec3 lambert = u_doDiffuse  * calcLambert(dirDiff, ndotl);
+	vec3 blinn   = u_doSpecular * calcBlinn(dirSpec, ndoth, ndotl, specPwr(gloss));
+	vec3 direct  = (lambert + blinn)*clight;
+
+	// Indirect lighting.
+	vec3 envSpec = envF0;
+	vec3 envDiff = albedo * 1.0-envF0;
+
+	// Note: Environment textures are filtered with cmft: https://github.com/dariomanesku/cmft
+	// Params used:
+	// --excludeBase true //!< First level mip is not filtered.
+	// --mipCount 7       //!< 7 mip levels are used in total, [256x256 .. 4x4]. Lower res mip maps should be avoided.
+	// --glossScale 10    //!< Spec power scale. See: specPwr().
+	// --glossBias 2      //!< Spec power bias. See: specPwr().
+	// --edgeFixup warp   //!< This must be used on DirectX9. When fileted with 'warp', fixCubeLookup() should be used.
+	float mip = 1.0 + 5.0*(1.0 - gloss); // Use mip levels [1..6] for radiance.
+
+	mat4 mtx;
+	mtx[0] = u_mtx0;
+	mtx[1] = u_mtx1;
+	mtx[2] = u_mtx2;
+	mtx[3] = u_mtx3;
+	vec3 vr = 2.0*ndotv*nn - vv; // Same as: -reflect(vv, nn);
+	vec3 cubeR = normalize(instMul(mtx, vec4(vr, 0.0)).xyz);
+	vec3 cubeN = normalize(instMul(mtx, vec4(nn, 0.0)).xyz);
+	cubeR = fixCubeLookup(cubeR, mip, 256.0);
+
+	vec3 radiance   = u_doDiffuseIbl  * envSpec * toLinear(textureCubeLod(s_texCube, cubeR, mip).xyz);
+	vec3 irradiance = u_doSpecularIbl * envDiff * toLinear(textureCube(s_texCubeIrr, cubeN).xyz);
+	vec3 indirect = radiance + irradiance;
+
+	// Color.
+	vec3 color = direct + indirect;
 	color = color * exp2(u_exposure);
-
 	gl_FragColor.xyz = toFilmic(color);
 	gl_FragColor.w = 1.0;
 }

+ 27 - 4
examples/18-ibl/fs_ibl_skybox.sc

@@ -1,22 +1,45 @@
 $input v_dir
 
 /*
- * Copyright 2014 Dario Manesku. All rights reserved.
+ * Copyright 2014-2016 Dario Manesku. All rights reserved.
  * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
  */
 
 #include "../common/common.sh"
+#include "uniforms.sh"
 
 SAMPLERCUBE(s_texCube, 0);
+SAMPLERCUBE(s_texCubeIrr, 1);
 
-uniform vec4 u_params;
-#define u_exposure  u_params.y
+// Ref: http://the-witness.net/news/2012/02/seamless-cube-map-filtering/
+vec3 fixCubeLookup(vec3 _v, float _lod, float _topLevelCubeSize)
+{
+	float ax = abs(_v.x);
+	float ay = abs(_v.y);
+	float az = abs(_v.z);
+	float vmax = max(max(ax, ay), az);
+	float scale = 1.0 - exp2(_lod) / _topLevelCubeSize;
+	if (ax != vmax) { _v.x *= scale; }
+	if (ay != vmax) { _v.y *= scale; }
+	if (az != vmax) { _v.z *= scale; }
+	return _v;
+}
 
 void main()
 {
 	vec3 dir = normalize(v_dir);
 
-	vec4 color = textureCubeLod(s_texCube, dir, 0.0);
+	vec4 color;
+	if (u_bgType == 7.0)
+	{
+		color = toLinear(textureCube(s_texCubeIrr, dir));
+	}
+	else
+	{
+		float lod = u_bgType;
+		dir = fixCubeLookup(dir, lod, 256.0);
+		color = toLinear(textureCubeLod(s_texCube, dir, lod));
+	}
 	color *= exp2(u_exposure);
 
 	gl_FragColor = toFilmic(color);

+ 511 - 231
examples/18-ibl/ibl.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Dario Manesku. All rights reserved.
+ * Copyright 2014-2016 Dario Manesku. All rights reserved.
  * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
  */
 
@@ -18,86 +18,51 @@ static float s_texelHalf = 0.0f;
 
 struct Uniforms
 {
-	void init()
-	{
-		m_time = 0.0f;
-		bx::mtxIdentity(m_mtx);
-
-		u_mtx     = bgfx::createUniform("u_mtx",     bgfx::UniformType::Mat4);
-		u_params  = bgfx::createUniform("u_params",  bgfx::UniformType::Vec4);
-		u_flags   = bgfx::createUniform("u_flags",   bgfx::UniformType::Vec4);
-		u_camPos  = bgfx::createUniform("u_camPos",  bgfx::UniformType::Vec4);
-		u_rgbDiff = bgfx::createUniform("u_rgbDiff", bgfx::UniformType::Vec4);
-		u_rgbSpec = bgfx::createUniform("u_rgbSpec", bgfx::UniformType::Vec4);
-	}
+	enum { NumVec4 = 12 };
 
-	// Call this once per frame.
-	void submitPerFrameUniforms()
+	void init()
 	{
-		bgfx::setUniform(u_mtx,     m_mtx);
-		bgfx::setUniform(u_flags,   m_flags);
-		bgfx::setUniform(u_camPos,  m_camPosTime);
-		bgfx::setUniform(u_rgbDiff, m_rgbDiff);
-		bgfx::setUniform(u_rgbSpec, m_rgbSpec);
+		u_params = bgfx::createUniform("u_params", bgfx::UniformType::Vec4, NumVec4);
 	}
 
-	// Call this before each draw call.
-	void submitPerDrawUniforms()
+	void submit()
 	{
-		bgfx::setUniform(u_params, m_params);
+		bgfx::setUniform(u_params, m_params, NumVec4);
 	}
 
 	void destroy()
 	{
-		bgfx::destroyUniform(u_rgbSpec);
-		bgfx::destroyUniform(u_rgbDiff);
-		bgfx::destroyUniform(u_camPos);
-		bgfx::destroyUniform(u_flags);
 		bgfx::destroyUniform(u_params);
-		bgfx::destroyUniform(u_mtx);
 	}
 
 	union
 	{
 		struct
 		{
-			float m_glossiness;
-			float m_exposure;
-			float m_diffspec;
-			float m_time;
-		};
-
-		float m_params[4];
-	};
-
-	union
-	{
-		struct
-		{
-			float m_diffuse;
-			float m_specular;
-			float m_diffuseIbl;
-			float m_specularIbl;
+			union
+			{
+				  float m_mtx[16];
+			/* 0*/ struct { float m_mtx0[4]; };
+			/* 1*/ struct { float m_mtx1[4]; };
+			/* 2*/ struct { float m_mtx2[4]; };
+			/* 3*/ struct { float m_mtx3[4]; };
+			};
+			/* 4*/ struct { float m_glossiness, m_reflectivity, m_exposure, m_bgType; };
+			/* 5*/ struct { float m_metalOrSpec, m_unused5[3]; };
+			/* 6*/ struct { float m_doDiffuse, m_doSpecular, m_doDiffuseIbl, m_doSpecularIbl; };
+			/* 7*/ struct { float m_cameraPos[3], m_unused7[1]; };
+			/* 8*/ struct { float m_rgbDiff[4]; };
+			/* 9*/ struct { float m_rgbSpec[4]; };
+			/*10*/ struct { float m_lightDir[3], m_unused10[1]; };
+			/*11*/ struct { float m_lightCol[3], m_unused11[1]; };
 		};
 
-		float m_flags[4];
+		float m_params[NumVec4*4];
 	};
 
-	float m_mtx[16];
-	float m_camPosTime[4];
-	float m_rgbDiff[4];
-	float m_rgbSpec[4];
-
-	bgfx::UniformHandle u_mtx;
 	bgfx::UniformHandle u_params;
-	bgfx::UniformHandle u_flags;
-	bgfx::UniformHandle u_camPos;
-	bgfx::UniformHandle u_rgbDiff;
-	bgfx::UniformHandle u_rgbSpec;
 };
 
-static Uniforms s_uniforms;
-
 struct PosColorTexCoord0Vertex
 {
 	float m_x;
@@ -181,11 +146,8 @@ struct LightProbe
 {
 	enum Enum
 	{
-		Wells,
-		Uffizi,
-		Pisa,
-		Ennis,
-		Grace,
+		Bolonga,
+		Kyoto,
 
 		Count
 	};
@@ -213,6 +175,253 @@ struct LightProbe
 	bgfx::TextureHandle m_texIrr;
 };
 
+struct Camera
+{
+	Camera()
+	{
+		reset();
+	}
+
+	void reset()
+	{
+		m_target.curr[0] = 0.0f;
+		m_target.curr[1] = 0.0f;
+		m_target.curr[2] = 0.0f;
+		m_target.dest[0] = 0.0f;
+		m_target.dest[1] = 0.0f;
+		m_target.dest[2] = 0.0f;
+
+		m_pos.curr[0] =  0.0f;
+		m_pos.curr[1] =  0.0f;
+		m_pos.curr[2] = -3.0f;
+		m_pos.dest[0] =  0.0f;
+		m_pos.dest[1] =  0.0f;
+		m_pos.dest[2] = -3.0f;
+
+		m_orbit[0] = 0.0f;
+		m_orbit[1] = 0.0f;
+	}
+
+	void mtxLookAt(float* _outViewMtx)
+	{
+		bx::mtxLookAt(_outViewMtx, m_pos.curr, m_target.curr);
+	}
+
+	void orbit(float _dx, float _dy)
+	{
+		m_orbit[0] += _dx;
+		m_orbit[1] += _dy;
+	}
+
+	void dolly(float _dz)
+	{
+		const float cnear = 1.0f;
+		const float cfar  = 10.0f;
+
+		const float toTarget[3] =
+		{
+			m_target.dest[0] - m_pos.dest[0],
+			m_target.dest[1] - m_pos.dest[1],
+			m_target.dest[2] - m_pos.dest[2],
+		};
+		const float toTargetLen = bx::vec3Length(toTarget);
+		const float invToTargetLen = 1.0f/(toTargetLen+FLT_MIN);
+		const float toTargetNorm[3] =
+		{
+			toTarget[0]*invToTargetLen,
+			toTarget[1]*invToTargetLen,
+			toTarget[2]*invToTargetLen,
+		};
+
+		float delta = toTargetLen*_dz;
+		float newLen = toTargetLen + delta;
+		if ( (cnear < newLen || _dz < 0.0f)
+		&&   (newLen < cfar  || _dz > 0.0f) )
+		{
+			m_pos.dest[0] += toTargetNorm[0]*delta;
+			m_pos.dest[1] += toTargetNorm[1]*delta;
+			m_pos.dest[2] += toTargetNorm[2]*delta;
+		}
+	}
+
+	void consumeOrbit(float _amount)
+	{
+		float consume[2];
+		consume[0] = m_orbit[0]*_amount;
+		consume[1] = m_orbit[1]*_amount;
+		m_orbit[0] -= consume[0];
+		m_orbit[1] -= consume[1];
+
+		const float toPos[3] =
+		{
+			m_pos.curr[0] - m_target.curr[0],
+			m_pos.curr[1] - m_target.curr[1],
+			m_pos.curr[2] - m_target.curr[2],
+		};
+		const float toPosLen = bx::vec3Length(toPos);
+		const float invToPosLen = 1.0f/(toPosLen+FLT_MIN);
+		const float toPosNorm[3] =
+		{
+			toPos[0]*invToPosLen,
+			toPos[1]*invToPosLen,
+			toPos[2]*invToPosLen,
+		};
+
+		float ll[2];
+		latLongFromVec(ll[0], ll[1], toPosNorm);
+		ll[0] += consume[0];
+		ll[1] -= consume[1];
+		ll[1] = bx::fclamp(ll[1], 0.02f, 0.98f);
+
+		float tmp[3];
+		vecFromLatLong(tmp, ll[0], ll[1]);
+
+		float diff[3];
+		diff[0] = (tmp[0]-toPosNorm[0])*toPosLen;
+		diff[1] = (tmp[1]-toPosNorm[1])*toPosLen;
+		diff[2] = (tmp[2]-toPosNorm[2])*toPosLen;
+
+		m_pos.curr[0] += diff[0];
+		m_pos.curr[1] += diff[1];
+		m_pos.curr[2] += diff[2];
+		m_pos.dest[0] += diff[0];
+		m_pos.dest[1] += diff[1];
+		m_pos.dest[2] += diff[2];
+	}
+
+	void update(float _dt)
+	{
+		const float amount = bx::fmin(_dt/0.12f, 1.0f);
+
+		consumeOrbit(amount);
+
+		m_target.curr[0] = bx::flerp(m_target.curr[0], m_target.dest[0], amount);
+		m_target.curr[1] = bx::flerp(m_target.curr[1], m_target.dest[1], amount);
+		m_target.curr[2] = bx::flerp(m_target.curr[2], m_target.dest[2], amount);
+		m_pos.curr[0] = bx::flerp(m_pos.curr[0], m_pos.dest[0], amount);
+		m_pos.curr[1] = bx::flerp(m_pos.curr[1], m_pos.dest[1], amount);
+		m_pos.curr[2] = bx::flerp(m_pos.curr[2], m_pos.dest[2], amount);
+	}
+
+	void envViewMtx(float* _mtx)
+	{
+		const float toTarget[3] =
+		{
+			m_target.curr[0] - m_pos.curr[0],
+			m_target.curr[1] - m_pos.curr[1],
+			m_target.curr[2] - m_pos.curr[2],
+		};
+
+		const float toTargetLen = bx::vec3Length(toTarget);
+		const float invToTargetLen = 1.0f/(toTargetLen+FLT_MIN);
+		const float toTargetNorm[3] =
+		{
+			toTarget[0]*invToTargetLen,
+			toTarget[1]*invToTargetLen,
+			toTarget[2]*invToTargetLen,
+		};
+
+		float tmp[3];
+		const float fakeUp[3] = { 0.0f, 1.0f, 0.0f };
+
+		float right[3];
+		bx::vec3Cross(tmp, fakeUp, toTargetNorm);
+		bx::vec3Norm(right, tmp);
+
+		float up[3];
+		bx::vec3Cross(tmp, toTargetNorm, right);
+		bx::vec3Norm(up, tmp);
+
+		_mtx[ 0] = right[0];
+		_mtx[ 1] = right[1];
+		_mtx[ 2] = right[2];
+		_mtx[ 3] = 0.0f;
+		_mtx[ 4] = up[0];
+		_mtx[ 5] = up[1];
+		_mtx[ 6] = up[2];
+		_mtx[ 7] = 0.0f;
+		_mtx[ 8] = toTargetNorm[0];
+		_mtx[ 9] = toTargetNorm[1];
+		_mtx[10] = toTargetNorm[2];
+		_mtx[11] = 0.0f;
+		_mtx[12] = 0.0f;
+		_mtx[13] = 0.0f;
+		_mtx[14] = 0.0f;
+		_mtx[15] = 1.0f;
+	}
+
+	static inline void vecFromLatLong(float _vec[3], float _u, float _v)
+	{
+		const float phi   = _u * 2.0f*bx::pi;
+		const float theta = _v * bx::pi;
+
+		const float st = bx::fsin(theta);
+		const float sp = bx::fsin(phi);
+		const float ct = bx::fcos(theta);
+		const float cp = bx::fcos(phi);
+
+		_vec[0] = -st*sp;
+		_vec[1] = ct;
+		_vec[2] = -st*cp;
+	}
+
+	static inline void latLongFromVec(float& _u, float& _v, const float _vec[3])
+	{
+		const float phi = atan2f(_vec[0], _vec[2]);
+		const float theta = acosf(_vec[1]);
+
+		_u = (bx::pi + phi)*bx::invPi*0.5f;
+		_v = theta*bx::invPi;
+	}
+
+	struct Interp3f
+	{
+		float curr[3];
+		float dest[3];
+	};
+
+	Interp3f m_target;
+	Interp3f m_pos;
+	float m_orbit[2];
+};
+
+struct Mouse
+{
+	Mouse()
+	{
+		m_dx = 0.0f;
+		m_dy = 0.0f;
+		m_prevMx = 0.0f;
+		m_prevMx = 0.0f;
+		m_scroll = 0;
+		m_scrollPrev = 0;
+	}
+
+	void update(float _mx, float _my, int32_t _mz, uint32_t _width, uint32_t _height)
+	{
+		const float widthf  = float(int32_t(_width));
+		const float heightf = float(int32_t(_height));
+
+		// Delta movement.
+		m_dx = float(_mx - m_prevMx)/widthf;
+		m_dy = float(_my - m_prevMy)/heightf;
+
+		m_prevMx = _mx;
+		m_prevMy = _my;
+
+		// Scroll.
+		m_scroll = _mz - m_scrollPrev;
+		m_scrollPrev = _mz;
+	}
+
+	float m_dx; // Screen space.
+	float m_dy;
+	float m_prevMx;
+	float m_prevMy;
+	int32_t m_scroll;
+	int32_t m_scrollPrev;
+};
+
 int _main_(int _argc, char** _argv)
 {
 	Args args(_argc, _argv);
@@ -240,18 +449,16 @@ int _main_(int _argc, char** _argv)
 	imguiCreate();
 
 	// Uniforms.
-	s_uniforms.init();
+	Uniforms uniforms;
+	uniforms.init();
 
 	// Vertex declarations.
 	PosColorTexCoord0Vertex::init();
 
 	LightProbe lightProbes[LightProbe::Count];
-	lightProbes[LightProbe::Wells ].load("wells");
-	lightProbes[LightProbe::Uffizi].load("uffizi");
-	lightProbes[LightProbe::Pisa  ].load("pisa");
-	lightProbes[LightProbe::Ennis ].load("ennis");
-	lightProbes[LightProbe::Grace ].load("grace");
-	LightProbe::Enum currentLightProbe = LightProbe::Wells;
+	lightProbes[LightProbe::Bolonga].load("bolonga");
+	lightProbes[LightProbe::Kyoto  ].load("kyoto");
+	LightProbe::Enum currentLightProbe = LightProbe::Bolonga;
 
 	bgfx::UniformHandle u_mtx        = bgfx::createUniform("u_mtx",        bgfx::UniformType::Mat4);
 	bgfx::UniformHandle u_params     = bgfx::createUniform("u_params",     bgfx::UniformType::Vec4);
@@ -266,43 +473,73 @@ int _main_(int _argc, char** _argv)
 	Mesh* meshBunny;
 	meshBunny = meshLoad("meshes/bunny.bin");
 
+	Mesh* meshOrb;
+	meshOrb = meshLoad("meshes/orb.bin");
+
+	Camera camera;
+	Mouse mouse;
+
 	struct Settings
 	{
-		float m_speed;
+		Settings()
+		{
+			m_envRotCurr = 0.0f;
+			m_envRotDest = 0.0f;
+			m_lightDir[0] = -0.8f;
+			m_lightDir[1] = 0.2f;
+			m_lightDir[2] = -0.5f;
+			m_lightCol[0] = 1.0f;
+			m_lightCol[1] = 1.0f;
+			m_lightCol[2] = 1.0f;
+			m_glossiness = 0.7f;
+			m_exposure = 0.0f;
+			m_bgType = 3.0f;
+			m_radianceSlider = 2.0f;
+			m_reflectivity = 0.85f;
+			m_rgbDiff[0] = 1.0f;
+			m_rgbDiff[1] = 1.0f;
+			m_rgbDiff[2] = 1.0f;
+			m_rgbSpec[0] = 1.0f;
+			m_rgbSpec[1] = 1.0f;
+			m_rgbSpec[2] = 1.0f;
+			m_lod = 0.0f;
+			m_doDiffuse = false;
+			m_doSpecular = false;
+			m_doDiffuseIbl = true;
+			m_doSpecularIbl = true;
+			m_showLightColorWheel = true;
+			m_showDiffColorWheel = true;
+			m_showSpecColorWheel = true;
+			m_metalOrSpec = 0;
+			m_meshSelection = 0;
+			m_crossCubemapPreview = ImguiCubemap::Latlong;
+		}
+
+		float m_envRotCurr;
+		float m_envRotDest;
+		float m_lightDir[3];
+		float m_lightCol[3];
 		float m_glossiness;
 		float m_exposure;
-		float m_diffspec;
+		float m_radianceSlider;
+		float m_bgType;
+		float m_reflectivity;
 		float m_rgbDiff[3];
 		float m_rgbSpec[3];
-		bool m_diffuse;
-		bool m_specular;
-		bool m_diffuseIbl;
-		bool m_specularIbl;
+		float m_lod;
+		bool m_doDiffuse;
+		bool m_doSpecular;
+		bool m_doDiffuseIbl;
+		bool m_doSpecularIbl;
+		bool m_showLightColorWheel;
 		bool m_showDiffColorWheel;
 		bool m_showSpecColorWheel;
+		uint8_t m_metalOrSpec;
+		uint8_t m_meshSelection;
 		ImguiCubemap::Enum m_crossCubemapPreview;
 	};
 
 	Settings settings;
-	settings.m_speed = 0.37f;
-	settings.m_glossiness = 1.0f;
-	settings.m_exposure = 0.0f;
-	settings.m_diffspec = 0.65f;
-	settings.m_rgbDiff[0] = 0.2f;
-	settings.m_rgbDiff[1] = 0.2f;
-	settings.m_rgbDiff[2] = 0.2f;
-	settings.m_rgbSpec[0] = 1.0f;
-	settings.m_rgbSpec[1] = 1.0f;
-	settings.m_rgbSpec[2] = 1.0f;
-	settings.m_diffuse = true;
-	settings.m_specular = true;
-	settings.m_diffuseIbl = true;
-	settings.m_specularIbl = true;
-	settings.m_showDiffColorWheel = true;
-	settings.m_showSpecColorWheel = false;
-	settings.m_crossCubemapPreview = ImguiCubemap::Cross;
-
-	float time = 0.0f;
 
 	int32_t leftScrollArea = 0;
 
@@ -320,127 +557,114 @@ int _main_(int _argc, char** _argv)
 			);
 
 		static int32_t rightScrollArea = 0;
-		imguiBeginScrollArea("Settings", width - 256 - 10, 10, 256, 540, &rightScrollArea);
-
-		imguiLabel("Shade:");
-		imguiSeparator();
-		imguiBool("Diffuse",      settings.m_diffuse);
-		imguiBool("Specular",     settings.m_specular);
-		imguiBool("IBL Diffuse",  settings.m_diffuseIbl);
-		imguiBool("IBL Specular", settings.m_specularIbl);
-
-		imguiSeparatorLine();
-		imguiSlider("Speed", settings.m_speed, 0.0f, 1.0f, 0.01f);
-		imguiSeparatorLine();
-
-		imguiSeparator();
-		imguiSlider("Exposure", settings.m_exposure, -8.0f, 8.0f, 0.01f);
-		imguiSeparator();
-
-		imguiLabel("Environment:");
-		currentLightProbe = LightProbe::Enum(imguiChoose(currentLightProbe
-													   , "Wells"
-													   , "Uffizi"
-													   , "Pisa"
-													   , "Ennis"
-													   , "Grace"
+		imguiBeginScrollArea("", width - 256 - 10, 10, 256, 700, &rightScrollArea);
+
+		imguiLabel("Environment light:");
+		imguiIndent();
+		imguiBool("IBL Diffuse",  settings.m_doDiffuseIbl);
+		imguiBool("IBL Specular", settings.m_doSpecularIbl);
+		currentLightProbe = LightProbe::Enum(imguiTabs(currentLightProbe, true, ImguiAlign::LeftIndented, 16, 2, 2
+													   , "Bolonga"
+													   , "Kyoto"
 													   ) );
-		static float lod = 0.0f;
-		if (imguiCube(lightProbes[currentLightProbe].m_tex, lod, settings.m_crossCubemapPreview, true) )
+		if (imguiCube(lightProbes[currentLightProbe].m_tex, settings.m_lod, settings.m_crossCubemapPreview, true) )
 		{
 			settings.m_crossCubemapPreview = ImguiCubemap::Enum( (settings.m_crossCubemapPreview+1) % ImguiCubemap::Count);
 		}
-		imguiSlider("Texture LOD", lod, 0.0f, 10.1f, 0.1f);
-
-		imguiEndScrollArea();
-
-		imguiBeginScrollArea("Settings", 10, 70, 256, 576, &leftScrollArea);
-
-		imguiLabel("Material properties:");
-		imguiSeparator();
-		imguiSlider("Diffuse - Specular", settings.m_diffspec,   0.0f, 1.0f, 0.01f);
-		imguiSlider("Glossiness"        , settings.m_glossiness, 0.0f, 1.0f, 0.01f);
-		imguiSeparator();
-
-		imguiColorWheel("Diffuse color:", &settings.m_rgbDiff[0], settings.m_showDiffColorWheel);
-		imguiSeparator();
-		imguiColorWheel("Specular color:", &settings.m_rgbSpec[0], settings.m_showSpecColorWheel);
-
-		imguiSeparator();
-		imguiLabel("Predefined materials:");
-		imguiSeparator();
-
-		if (imguiButton("Gold") )
+		imguiSlider("Texture LOD", settings.m_lod, 0.0f, 10.1f, 0.1f);
+		imguiUnindent();
+
+		imguiSeparator(8);
+		imguiLabel("Directional light:");
+		imguiIndent();
+		imguiBool("Diffuse",  settings.m_doDiffuse);
+		imguiBool("Specular", settings.m_doSpecular);
+		const bool doDirectLighting = settings.m_doDiffuse || settings.m_doSpecular;
+		imguiSlider("Light direction X", settings.m_lightDir[0], -1.0f, 1.0f, 0.1f, doDirectLighting);
+		imguiSlider("Light direction Y", settings.m_lightDir[1], -1.0f, 1.0f, 0.1f, doDirectLighting);
+		imguiSlider("Light direction Z", settings.m_lightDir[2], -1.0f, 1.0f, 0.1f, doDirectLighting);
+		imguiColorWheel("Color:", settings.m_lightCol, settings.m_showLightColorWheel, 0.6f, doDirectLighting);
+		imguiUnindent();
+
+		imguiSeparator(8);
+		imguiLabel("Background:");
+		imguiIndent();
 		{
-			settings.m_glossiness = 0.8f;
-			settings.m_diffspec   = 1.0f;
-
-			settings.m_rgbDiff[0] = 0.0f;
-			settings.m_rgbDiff[1] = 0.0f;
-			settings.m_rgbDiff[2] = 0.0f;
-
-			settings.m_rgbSpec[0] = 1.0f;
-			settings.m_rgbSpec[1] = 0.86f;
-			settings.m_rgbSpec[2] = 0.58f;
+			int32_t selection;
+			if      (0.0f == settings.m_bgType) { selection = 0; }
+			else if (7.0f == settings.m_bgType) { selection = 2; }
+			else                                { selection = 1; }
+			selection = imguiTabs(selection, true, ImguiAlign::LeftIndented, 16, 2, 3
+								 , "Skybox"
+								 , "Radiance"
+								 , "Irradiance"
+								 );
+			if      (0 == selection) { settings.m_bgType = 0.0f; }
+			else if (2 == selection) { settings.m_bgType = 7.0f; }
+			else                     { settings.m_bgType = settings.m_radianceSlider; }
+			const bool isRadiance = (selection == 1);
+			imguiSlider("Mip level", settings.m_radianceSlider, 1.0f, 6.0f, 0.1f, isRadiance);
 		}
+		imguiUnindent();
 
-		if (imguiButton("Copper") )
-		{
-			settings.m_glossiness = 0.67f;
-			settings.m_diffspec   = 1.0f;
-
-			settings.m_rgbDiff[0] = 0.0f;
-			settings.m_rgbDiff[1] = 0.0f;
-			settings.m_rgbDiff[2] = 0.0f;
+		imguiSeparator(8);
+		imguiLabel("Post processing:");
+		imguiIndent();
+		imguiSlider("Exposure", settings.m_exposure, -4.0f, 4.0f, 0.1f);
+		imguiUnindent();
 
-			settings.m_rgbSpec[0] = 0.98f;
-			settings.m_rgbSpec[1] = 0.82f;
-			settings.m_rgbSpec[2] = 0.76f;
-		}
+		imguiSeparator();
 
-		if (imguiButton("Titanium") )
-		{
-			settings.m_glossiness = 0.57f;
-			settings.m_diffspec   = 1.0f;
+		imguiEndScrollArea();
 
-			settings.m_rgbDiff[0] = 0.0f;
-			settings.m_rgbDiff[1] = 0.0f;
-			settings.m_rgbDiff[2] = 0.0f;
+		imguiBeginScrollArea("", 10, 70, 256, 636, &leftScrollArea);
 
-			settings.m_rgbSpec[0] = 0.76f;
-			settings.m_rgbSpec[1] = 0.73f;
-			settings.m_rgbSpec[2] = 0.71f;
-		}
+		imguiLabel("Mesh:");
+		imguiIndent();
+		settings.m_meshSelection = imguiChoose(settings.m_meshSelection, "Bunny", "Orbs");
+		imguiUnindent();
 
-		if (imguiButton("Steel") )
+		const bool isBunny = (0 == settings.m_meshSelection);
+		if (!isBunny)
 		{
-			settings.m_glossiness = 0.82f;
-			settings.m_diffspec   = 1.0f;
-
-			settings.m_rgbDiff[0] = 0.0f;
-			settings.m_rgbDiff[1] = 0.0f;
-			settings.m_rgbDiff[2] = 0.0f;
-
-			settings.m_rgbSpec[0] = 0.77f;
-			settings.m_rgbSpec[1] = 0.78f;
-			settings.m_rgbSpec[2] = 0.77f;
+			settings.m_metalOrSpec = 0.0f;
 		}
 
+		imguiSeparator(4);
+		imguiLabel("Workflow:");
+		imguiIndent();
+		if (imguiCheck("Metalness", 0 == settings.m_metalOrSpec, isBunny) ) { settings.m_metalOrSpec = 0; }
+		if (imguiCheck("Specular",  1 == settings.m_metalOrSpec, isBunny) ) { settings.m_metalOrSpec = 1; }
+		imguiUnindent();
+
+		imguiSeparator(4);
+		imguiLabel("Material:");
+		imguiIndent();
+		imguiSlider("Glossiness", settings.m_glossiness, 0.0f, 1.0f, 0.01f, isBunny);
+		imguiSlider(0 == settings.m_metalOrSpec ? "Metalness" : "Diffuse - Specular", settings.m_reflectivity, 0.0f, 1.0f, 0.01f, isBunny);
+		imguiUnindent();
+
+		imguiColorWheel("Diffuse:", &settings.m_rgbDiff[0], settings.m_showDiffColorWheel, 0.7f);
+		imguiSeparator();
+		imguiColorWheel("Specular:", &settings.m_rgbSpec[0], settings.m_showSpecColorWheel, 0.7f, (1 == settings.m_metalOrSpec) && isBunny);
+
 		imguiEndScrollArea();
 
 		imguiEndFrame();
 
-		s_uniforms.m_glossiness = settings.m_glossiness;
-		s_uniforms.m_exposure = settings.m_exposure;
-		s_uniforms.m_diffspec = settings.m_diffspec;
-		s_uniforms.m_flags[0] = float(settings.m_diffuse);
-		s_uniforms.m_flags[1] = float(settings.m_specular);
-		s_uniforms.m_flags[2] = float(settings.m_diffuseIbl);
-		s_uniforms.m_flags[3] = float(settings.m_specularIbl);
-		memcpy(s_uniforms.m_rgbDiff, settings.m_rgbDiff, 3*sizeof(float) );
-		memcpy(s_uniforms.m_rgbSpec, settings.m_rgbSpec, 3*sizeof(float) );
-
-		s_uniforms.submitPerFrameUniforms();
+		uniforms.m_glossiness   = settings.m_glossiness;
+		uniforms.m_reflectivity = settings.m_reflectivity;
+		uniforms.m_exposure     = settings.m_exposure;
+		uniforms.m_bgType       = settings.m_bgType;
+		uniforms.m_metalOrSpec   = float(settings.m_metalOrSpec);
+		uniforms.m_doDiffuse     = float(settings.m_doDiffuse);
+		uniforms.m_doSpecular    = float(settings.m_doSpecular);
+		uniforms.m_doDiffuseIbl  = float(settings.m_doDiffuseIbl);
+		uniforms.m_doSpecularIbl = float(settings.m_doSpecularIbl);
+		memcpy(uniforms.m_rgbDiff,  settings.m_rgbDiff,  3*sizeof(float) );
+		memcpy(uniforms.m_rgbSpec,  settings.m_rgbSpec,  3*sizeof(float) );
+		memcpy(uniforms.m_lightDir, settings.m_lightDir, 3*sizeof(float) );
+		memcpy(uniforms.m_lightCol, settings.m_lightCol, 3*sizeof(float) );
 
 		int64_t now = bx::getHPCounter();
 		static int64_t last = now;
@@ -448,9 +672,7 @@ int _main_(int _argc, char** _argv)
 		last = now;
 		const double freq = double(bx::getHPFrequency() );
 		const double toMs = 1000.0/freq;
-
-		time += (float)(frameTime*settings.m_speed/freq);
-		s_uniforms.m_camPosTime[3] = time;
+		const double deltaTimeSec = double(frameTime)/freq;
 
 		// Use debug font to print information about this example.
 		bgfx::dbgTextClear();
@@ -458,53 +680,110 @@ int _main_(int _argc, char** _argv)
 		bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Image-based lighting.");
 		bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);
 
-		float at[3] = { 0.0f, 0.0f, 0.0f };
-		float eye[3] = { 0.0f, 0.0f, -3.0f };
-
-		bx::mtxRotateXY(s_uniforms.m_mtx
-			, 0.0f
-			, time
-			);
+		// Camera.
+		const bool mouseOverGui = imguiMouseOverArea();
+		mouse.update(mouseState.m_mx, mouseState.m_my, mouseState.m_mz, width, height);
+		if (!mouseOverGui)
+		{
+			if (mouseState.m_buttons[entry::MouseButton::Left])
+			{
+				camera.orbit(mouse.m_dx, mouse.m_dy);
+			}
+			else if (mouseState.m_buttons[entry::MouseButton::Right])
+			{
+				camera.dolly(mouse.m_dx + mouse.m_dy);
+			}
+			else if (mouseState.m_buttons[entry::MouseButton::Middle])
+			{
+				settings.m_envRotDest += mouse.m_dx*2.0f;
+			}
+			else if (0 != mouse.m_scroll)
+			{
+				camera.dolly(float(mouse.m_scroll)*0.05f);
+			}
+		}
+		camera.update(deltaTimeSec);
+		memcpy(uniforms.m_cameraPos, camera.m_pos.curr, 3*sizeof(float) );
 
+		// View Transform 0.
 		float view[16];
 		float proj[16];
-
 		bx::mtxIdentity(view);
 		bx::mtxOrtho(proj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f);
 		bgfx::setViewTransform(0, view, proj);
 
-		bx::mtxLookAt(view, eye, at);
-		memcpy(s_uniforms.m_camPosTime, eye, 3*sizeof(float) );
-		bx::mtxProj(proj, 60.0f, float(width)/float(height), 0.1f, 100.0f);
+		// View Transform 1.
+		camera.mtxLookAt(view);
+		bx::mtxProj(proj, 45.0f, float(width)/float(height), 0.1f, 100.0f);
 		bgfx::setViewTransform(1, view, proj);
 
+		// View rect.
 		bgfx::setViewRect(0, 0, 0, width, height);
 		bgfx::setViewRect(1, 0, 0, width, height);
 
-		// View 0.
+		// Env rotation.
+		const float amount = bx::fmin(deltaTimeSec/0.12f, 1.0f);
+		settings.m_envRotCurr = bx::flerp(settings.m_envRotCurr, settings.m_envRotDest, amount);
+
+		// Env mtx.
+		float mtxEnvView[16];
+		camera.envViewMtx(mtxEnvView);
+		float mtxEnvRot[16];
+		bx::mtxRotateY(mtxEnvRot, settings.m_envRotCurr);
+		bx::mtxMul(uniforms.m_mtx, mtxEnvView, mtxEnvRot);
+
+		// Submit view 0.
 		bgfx::setTexture(0, s_texCube, lightProbes[currentLightProbe].m_tex);
 		bgfx::setState(BGFX_STATE_RGB_WRITE|BGFX_STATE_ALPHA_WRITE);
 		screenSpaceQuad( (float)width, (float)height, true);
-		s_uniforms.submitPerDrawUniforms();
+		uniforms.submit();
 		bgfx::submit(0, programSky);
 
-		// View 1.
-		float mtx[16];
-		bx::mtxSRT(mtx
-				, 1.0f
-				, 1.0f
-				, 1.0f
-				, 0.0f
-				, bx::pi+time
-				, 0.0f
-				, 0.0f
-				, -1.0f
-				, 0.0f
-				);
-
-		bgfx::setTexture(0, s_texCube,    lightProbes[currentLightProbe].m_tex);
-		bgfx::setTexture(1, s_texCubeIrr, lightProbes[currentLightProbe].m_texIrr);
-		meshSubmit(meshBunny, 1, programMesh, mtx);
+		// Submit view 1.
+		if (0 == settings.m_meshSelection)
+		{
+			// Submit bunny.
+			float mtx[16];
+			bx::mtxSRT(mtx, 1.0f, 1.0f, 1.0f, 0.0f, bx::pi, 0.0f, 0.0f, -0.80f, 0.0f);
+			bgfx::setTexture(0, s_texCube,    lightProbes[currentLightProbe].m_tex);
+			bgfx::setTexture(1, s_texCubeIrr, lightProbes[currentLightProbe].m_texIrr);
+			meshSubmit(meshBunny, 1, programMesh, mtx);
+		}
+		else
+		{
+			// Submit orbs.
+			for (float yy = 0, yend = 5.0f; yy < yend; yy+=1.0f)
+			{
+				for (float xx = 0, xend = 5.0f; xx < xend; xx+=1.0f)
+				{
+					const float scale   =  1.2f;
+					const float spacing =  2.2f;
+					const float yAdj    = -0.8f;
+
+					float mtx[16];
+					bx::mtxSRT(mtx
+							, scale/xend
+							, scale/xend
+							, scale/xend
+							, 0.0f
+							, 0.0f
+							, 0.0f
+							, 0.0f      + (xx/xend)*spacing - (1.0f + (scale-1.0f)*0.5f - 1.0f/xend)
+							, yAdj/yend + (yy/yend)*spacing - (1.0f + (scale-1.0f)*0.5f - 1.0f/yend)
+							, 0.0f
+							);
+
+					uniforms.m_glossiness   =        xx*(1.0f/xend);
+					uniforms.m_reflectivity = (yend-yy)*(1.0f/yend);
+					uniforms.m_metalOrSpec = 0.0f;
+					uniforms.submit();
+
+					bgfx::setTexture(0, s_texCube,    lightProbes[currentLightProbe].m_tex);
+					bgfx::setTexture(1, s_texCubeIrr, lightProbes[currentLightProbe].m_texIrr);
+					meshSubmit(meshOrb, 1, programMesh, mtx);
+				}
+			}
+		}
 
 		// Advance to next frame. Rendering thread will be kicked to
 		// process submitted rendering primitives.
@@ -512,6 +791,7 @@ int _main_(int _argc, char** _argv)
 	}
 
 	meshUnload(meshBunny);
+	meshUnload(meshOrb);
 
 	// Cleanup.
 	bgfx::destroyProgram(programMesh);
@@ -530,7 +810,7 @@ int _main_(int _argc, char** _argv)
 		lightProbes[ii].destroy();
 	}
 
-	s_uniforms.destroy();
+	uniforms.destroy();
 
 	imguiDestroy();
 

+ 28 - 0
examples/18-ibl/uniforms.sh

@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Dario Manesku. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+uniform vec4 u_params[12];
+#define u_mtx0          u_params[0]
+#define u_mtx1          u_params[1]
+#define u_mtx2          u_params[2]
+#define u_mtx3          u_params[3]
+#define u_glossiness    u_params[4].x
+#define u_reflectivity  u_params[4].y
+#define u_exposure      u_params[4].z
+#define u_bgType        u_params[4].w
+#define u_metalOrSpec   u_params[5].x
+#define u_unused        u_params[5].yzw
+#define u_doDiffuse     u_params[6].x
+#define u_doSpecular    u_params[6].y
+#define u_doDiffuseIbl  u_params[6].z
+#define u_doSpecularIbl u_params[6].w
+#define u_camPos        u_params[7].xyz
+#define u_unused7       u_params[7].w
+#define u_rgbDiff       u_params[8]
+#define u_rgbSpec       u_params[9]
+#define u_lightDir      u_params[10].xyz
+#define u_unused10      u_params[10].w
+#define u_lightCol      u_params[11].xyz
+#define u_unused11      u_params[11].w

+ 4 - 4
examples/18-ibl/vs_ibl_mesh.sc

@@ -2,19 +2,19 @@ $input a_position, a_normal
 $output v_view, v_normal
 
 /*
- * Copyright 2014 Dario Manesku. All rights reserved.
+ * Copyright 2014-2016 Dario Manesku. All rights reserved.
  * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
  */
 
 #include "../common/common.sh"
-
-uniform vec4 u_camPos;
+#include "uniforms.sh"
 
 void main()
 {
 	gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0) );
 
+	v_view = u_camPos - mul(u_model[0], vec4(a_position, 1.0)).xyz;
+
 	vec3 normal = a_normal * 2.0 - 1.0;
 	v_normal = mul(u_model[0], vec4(normal, 0.0) ).xyz;
-	v_view = normalize(u_camPos.xyz - mul(u_model[0], vec4(a_position, 1.0)).xyz);
 }

+ 13 - 3
examples/18-ibl/vs_ibl_skybox.sc

@@ -2,11 +2,12 @@ $input a_position, a_texcoord0
 $output v_dir
 
 /*
- * Copyright 2014 Dario Manesku. All rights reserved.
+ * Copyright 2014-2016 Dario Manesku. All rights reserved.
  * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
  */
 
 #include "../common/common.sh"
+#include "uniforms.sh"
 
 uniform mat4 u_mtx;
 
@@ -14,6 +15,15 @@ void main()
 {
 	gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0) );
 
-	vec2 tex = 2.0 * a_texcoord0 - 1.0;
-	v_dir = mul(u_mtx, vec4(tex, 1.0, 0.0) ).xyz;
+	float fov = 45.0;
+	float height = tan(fov*0.5);
+	float aspect = height*(4.0/3.0);
+	vec2 tex = (2.0*a_texcoord0-1.0) * vec2(aspect, height);
+
+	mat4 mtx;
+	mtx[0] = u_mtx0;
+	mtx[1] = u_mtx1;
+	mtx[2] = u_mtx2;
+	mtx[3] = u_mtx3;
+	v_dir = instMul(mtx, vec4(tex, 1.0, 0.0) ).xyz;
 }

二进制
examples/runtime/shaders/glsl/fs_ibl_mesh.bin


二进制
examples/runtime/shaders/glsl/fs_ibl_skybox.bin


二进制
examples/runtime/shaders/glsl/vs_ibl_mesh.bin


二进制
examples/runtime/shaders/glsl/vs_ibl_skybox.bin


二进制
examples/runtime/textures/bolonga_irr.dds


二进制
examples/runtime/textures/bolonga_lod.dds


二进制
examples/runtime/textures/ennis_irr.dds


二进制
examples/runtime/textures/ennis_lod.dds


二进制
examples/runtime/textures/grace_irr.dds


二进制
examples/runtime/textures/grace_lod.dds


二进制
examples/runtime/textures/kyoto_irr.dds


二进制
examples/runtime/textures/kyoto_lod.dds


二进制
examples/runtime/textures/pisa_irr.dds


二进制
examples/runtime/textures/pisa_lod.dds


二进制
examples/runtime/textures/uffizi_irr.dds


二进制
examples/runtime/textures/uffizi_lod.dds


二进制
examples/runtime/textures/wells_irr.dds


二进制
examples/runtime/textures/wells_lod.dds