local glsl = {} local lightPosition = {} local headsetPosition = {} local controller = nil function lovr.load() model = lovr.graphics.newModel('helmet/DamagedHelmet.gltf') shader = lovr.graphics.newShader(unpack(glsl.pbr)) lovr.graphics.setBackgroundColor(.18, .18, .20) lightPosition = { 75, 60, 20 } end function lovr.update(dt) controller = lovr.headset.getControllers()[1] if controller then lightPosition = { controller:getPosition() } end headsetPosition = { lovr.headset.getPosition() } shader:send('lightPosition', lightPosition) shader:send('headsetPosition', headsetPosition) end function lovr.draw() lovr.graphics.setShader(shader) model:draw(0, 1.6, -1.5, .4, lovr.timer.getTime() * .12 + .1) lovr.graphics.setShader() if controller then local x, y, z = unpack(lightPosition) lovr.graphics.setColor(1, 1, 1) lovr.graphics.sphere(x, y, z, .01) end end glsl.pbr = { [[ out vec3 vNormal; out vec3 vVertex; vec4 position(mat4 projection, mat4 transform, vec4 vertex) { vNormal = mat3(lovrModel) * lovrNormal; vVertex = vec3(lovrModel * vertex); return projection * lovrView * vec4(vVertex, 1.0); } ]], [[ #define PI 3.141592653 in vec3 vNormal; in vec3 vVertex; uniform vec3 headsetPosition; uniform vec3 lightPosition; float D_GGX(float NoH, float roughness) { float alpha = roughness * roughness; float alpha2 = alpha * alpha; float denom = (NoH * NoH) * (alpha2 - 1.) + 1.; return alpha2 / (PI * denom * denom); } float G_SmithGGXCorrelated(float NoV, float NoL, float roughness) { float alpha = roughness * roughness; float alpha2 = alpha * alpha; float GGXV = NoL * sqrt(alpha2 + (1. - alpha2) * (NoV * NoV)); float GGXL = NoV * sqrt(alpha2 + (1. - alpha2) * (NoL * NoL)); return .5 / max(GGXV + GGXL, 1e-5); } vec3 F_Schlick(vec3 F0, float LoH) { return F0 + (vec3(1.) - F0) * pow(1. - LoH, 5.); } vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) { vec3 baseColor = texture(lovrDiffuseTexture, uv).rgb; vec3 emissive = texture(lovrEmissiveTexture, uv).rgb; float metalness = texture(lovrMetalnessTexture, uv).b * lovrMetalness; float roughness = max(texture(lovrRoughnessTexture, uv).g * lovrRoughness, .05); float occlusion = texture(lovrOcclusionTexture, uv).r; vec3 F0 = mix(vec3(.04), baseColor, metalness); vec3 N = normalize(vNormal); vec3 V = normalize(headsetPosition - vVertex); vec3 L = normalize(lightPosition - vVertex); vec3 H = normalize(V + L); float NoV = abs(dot(N, V)) + 1e-5; float NoL = clamp(dot(N, L), 0., 1.); float NoH = clamp(dot(N, H), 0., 1.); float LoH = clamp(dot(L, H), 0., 1.); float D = D_GGX(NoH, roughness); float G = G_SmithGGXCorrelated(NoV, NoL, roughness); vec3 F = F_Schlick(F0, LoH); vec3 specular = vec3(D * G * F); vec3 diffuse = (vec3(1.) - F) * (1. - metalness) * baseColor; vec3 color = (diffuse + specular) * NoL * occlusion + emissive; ]] .. (lovr.getOS() == 'Web' and ' color = pow(color, vec3(.4545));\n' or '') .. [[ return vec4(vec3(color), 1.); } ]] }