$input a_position, a_texcoord0, a_normal $output v_texcoord0, v_color0, v_texcoord1, v_worldPos, v_position, v_start, v_end, v_centerRadius #include "common.sh" uniform vec4 u_color; uniform vec4 u_pixelInfo; // x -> Fov * 0.5f / screenSize.y; y -> ScreenWidth; z -> ScreenWidth / ScreenHeight; w -> Line thickness SAMPLER2D(s_diffuseMap, 0); // Vertex Shaders void AdornSelfLitVS() { vec4 worldPos = mul(u_worldMatrix, vec4(a_position, 1.0)); vec3 worldNormal = normalize(mul(u_worldMatrix, vec4(a_normal, 0.0)).xyz); vec3 light = normalize(u_cameraPosition - worldPos.xyz); float ndotl = saturate(dot(worldNormal, light)); float lighting = 0.5 + (1.0 - 0.5) * ndotl; float specular = pow(ndotl, 64.0); gl_Position = mul(u_viewProjection, worldPos); v_texcoord0 = a_texcoord0; v_color0 = vec4(u_color.rgb * lighting + vec3(specular), u_color.a); v_texcoord1.x = (u_fogParams.z - gl_Position.w) * u_fogParams.w; } void AdornSelfLitHighlightVS() { vec4 worldPos = mul(u_worldMatrix, vec4(a_position, 1.0)); vec3 worldNormal = normalize(mul(u_worldMatrix, vec4(a_normal, 0.0)).xyz); vec3 light = normalize(u_cameraPosition - worldPos.xyz); float ndotl = saturate(dot(worldNormal, light)); float lighting = 0.75 + (1.0 - 0.75) * ndotl; float specular = pow(ndotl, 64.0); gl_Position = mul(u_viewProjection, worldPos); v_texcoord0 = a_texcoord0; v_color0 = vec4(u_color.rgb * lighting + vec3(specular), u_color.a); v_texcoord1.x = (u_fogParams.z - gl_Position.w) * u_fogParams.w; } void AdornVS() { vec4 worldPos = mul(u_worldMatrix, vec4(a_position, 1.0)); #ifdef PIN_LIGHTING vec3 worldNormal = normalize(mul(u_worldMatrix, vec4(a_normal, 0.0)).xyz); float ndotl = dot(worldNormal, -u_lamp0Dir); vec3 lighting = u_ambientColor + saturate(ndotl) * u_lamp0Color + saturate(-ndotl) * u_lamp1Color; #else vec3 lighting = vec3(1.0); #endif gl_Position = mul(u_viewProjection, worldPos); v_texcoord0 = a_texcoord0; v_color0 = vec4(u_color.rgb * lighting, u_color.a); v_texcoord1.x = (u_fogParams.z - gl_Position.w) * u_fogParams.w; } void AdornAALineVS() { vec4 worldPos = mul(u_worldMatrix, vec4(a_position, 1.0)); vec3 worldNormal = normalize(mul(u_worldMatrix, vec4(a_normal, 0.0)).xyz); // line start and end position in world space vec4 startPosW = mul(u_worldMatrix, vec4(1.0, 0.0, 0.0, 1.0)); vec4 endPosW = mul(u_worldMatrix, vec4(-1.0, 0.0, 0.0, 1.0)); // Compute view-space w float w = dot(u_viewProjection[3], vec4(worldPos.xyz, 1.0)); // radius in pixels + constant because line has to be little bit bigger to perform anti aliasing float radius = u_pixelInfo.w + 2.0; // scale the way that line has same size on screen if (length(worldPos.xyz - startPosW.xyz) < length(worldPos.xyz - endPosW.xyz)) { float w = dot(u_viewProjection[3], vec4(startPosW.xyz, 1.0)); float pixel_radius = radius * w * u_pixelInfo.x; worldPos.xyz = startPosW.xyz + worldNormal * pixel_radius; } else { float w = dot(u_viewProjection[3], vec4(endPosW.xyz, 1.0)); float pixel_radius = radius * w * u_pixelInfo.x; worldPos.xyz = endPosW.xyz + worldNormal * pixel_radius; } // output for PS gl_Position = mul(u_viewProjection, worldPos); v_position = gl_Position.xyw; v_start = mul(u_viewProjection, startPosW); v_end = mul(u_viewProjection, endPosW); v_texcoord1.x = (u_fogParams.z - gl_Position.w) * u_fogParams.w; // screen ratio v_position.y *= u_pixelInfo.z; v_start.y *= u_pixelInfo.z; v_end.y *= u_pixelInfo.z; } void AdornOutlineVS() { vec4 worldPos = mul(u_worldMatrix, vec4(a_position, 1.0)); gl_Position = mul(u_viewProjection, worldPos); v_color0 = u_color; v_worldPos = worldPos.xyz; v_centerRadius = vec4(mul(u_worldMatrix, vec4(0.0, 0.0, 0.0, 1.0)).xyz, length(mul(u_worldMatrix, vec4(1.0, 0.0, 0.0, 0.0)).xyz)); } // Pixel/Fragment Shaders void AdornPS() { vec4 result = texture2D(s_diffuseMap, v_texcoord0) * v_color0; result.rgb = mix(u_fogColor, result.rgb, saturate(v_texcoord1.x)); gl_FragColor = result; } void AdornAALinePS() { vec3 position = v_position / v_position.z; vec4 start = v_start / v_start.w; vec4 end = v_end / v_end.w; vec2 lineDir = normalize(end.xy - start.xy); vec2 fragToPoint = position.xy - start.xy; // tips of the line are not Anti-Aliased, they are just cut // discard as soon as we can float startDist = dot(lineDir, fragToPoint); float endDist = dot(lineDir, -position.xy + end.xy); if (startDist < 0.0) discard; if (endDist < 0.0) discard; vec2 perpLineDir = vec2(lineDir.y, -lineDir.x); float dist = abs(dot(perpLineDir, fragToPoint)); // high point serves to compute the function which is described below float highPoint = 1.0 + (u_pixelInfo.w - 1.0) * 0.5; // this is function that has this shape /¯¯¯\, it is symmetric, centered around 0 on X axis // slope parts are +- 45 degree and are 1px thick. Area of the shape sums to line thickness in pixels // function for 1px would be /\, func for 2px is /¯\ and so on... vec4 result = vec4(1.0); result.a = saturate(highPoint - (dist * 0.5 * u_pixelInfo.y)); result *= u_color; // convert to sRGB, its not perfect for non-black backgrounds, but its the best we can get result.a = pow(saturate(1.0 - result.a), 1.0/2.2); result.a = 1.0 - result.a; result.rgb = mix(u_fogColor, result.rgb, saturate(v_texcoord1.x)); gl_FragColor = result; } void AdornOutlinePS() { vec3 rayO = v_worldPos - v_centerRadius.xyz; vec3 rayD = normalize(v_worldPos - u_cameraPosition); // magnitude(rayO + t * rayD) = radius // t^2 + bt + c = radius float thickness = 1.0; float r0 = v_centerRadius.w; float r1 = max(0.0, v_centerRadius.w - thickness); float b = 2.0 * dot(rayO, rayD); float c0 = dot(rayO, rayO) - r0 * r0; float c1 = dot(rayO, rayO) - r1 * r1; if (b * b < 4.0 * c0) discard; if (b * b > 4.0 * c1) discard; gl_FragColor = v_color0; }