The newer 3D additions to C3 have opened up many visual possibilities to doing 3D games + C3 2D game logic / collision, etc.
One drawback for the 3D rendering has been the issue that there is no lighting, so the 3D objects can sometimes look flat, losing the feeling of depth, because all the sides of a 3D shape have the same intensity and they can blend together.
I would like to suggest one idea that could help, add a single light lighting shader as an option for the default vertex and fragment shaders that are using during predraw of 3D Shapes or 3D Meshes. Doing it on the GPU per fragment can make the lighting more subtle and does not take up CPU performance.
To test this out just for proof of concept, I changed the C3 default vertex and fragment shaders. The vertex shader was changed to pass the position. The fragment shader was changed to calculate the fragment normal from pos based on dfdy and dfdx and then simple point / spotlight lighting with attenuation was applied (with a little bonus of creating normals from texture changes to make surfaces look a little more interesting, this could be removed.)
It looks pretty decent and adds a nice feeling of depth to the 3D Shape and Mesh geometry.
I think it could get complicated with more lights, but being able to define a single light would go a long way.
Below is an example based on the latest complex terrain example (w/ higher resolution viewport and textures.)
These could be applied when doing the pre draw for Mesh and 3D types of objects (3DShape and others).
Ashley - if you want to do some quick testing, here is the simple test code, you can also contact me to get a test project if you are interested.
return [`#version 300 es`, `in highp vec3 aPos;`, `in ${texPrecision} vec2 aTex;`, `out ${texPrecision} vec2 vTex;`, `out highp vec3 pos;`, `uniform highp mat4 matP;`, `uniform highp mat4 matMV;`, `void main(void) {`, ` gl_Position = matP * matMV * vec4(aPos, 1.0);`, ` vTex = aTex;`, ` pos = aPos;`, `}`].join("\n")
return[
"#version 300 es",
"in mediump vec2 vTex;in highp vec3 pos;",
"out lowp vec4 outColor;",
"uniform lowp vec4 color;",
"uniform lowp sampler2D samplerFront;",
"uniform highp vec2 pixelSize;",
"highp float luminance(in highp vec3 c)",
"{",
" return dot(c, vec3(.2126, .7152, .0722));",
"}",
"highp vec3 normalColor(in highp vec2 uv, sampler2D nSampler)",
"{",
"highp vec2 s = pixelSize;",
" const highp vec2 size = vec2(2.0,0.0);",
" const highp vec3 off = vec3(-1.,0.,1.);",
"highp float s11 = luminance(texture(nSampler,uv).xyz);",
"highp float s01 = luminance(texture(nSampler, uv+off.xy*0.0001).xyz);",
"highp float s10 = luminance(texture(nSampler, uv+off.yx*0.0001).xyz);",
"highp vec3 va = (vec3(size.xy*0.001, s01 - s11));",
"highp vec3 vb = (vec3(size.yx*0.001, s10 - s11));",
"highp vec3 normalV = normalize(cross(va, vb));",
" return normalV;",
"}",
"void main(void) {",
"highp vec3 lightPos = vec3(300.,100.,800.);",
"highp vec3 lightDir = pos-lightPos;",
"highp float lightDist = length(lightDir);",
"lightDir = normalize(lightDir);",
"highp vec3 spotDir = vec3(0,0.7,-1.);",
"spotDir = normalize(spotDir);",
"highp vec3 dx = dFdx(pos);",
"highp vec3 dy = dFdy(pos);",
"highp vec3 worldSpaceNormal = normalize(cross(dx, dy));",
"worldSpaceNormal = worldSpaceNormal + 0.15 * normalColor(vTex, samplerFront);",
"worldSpaceNormal = normalize(worldSpaceNormal);",
"lowp vec4 tex = texture(samplerFront, vTex);",
"highp float light = dot(worldSpaceNormal,lightDir) * 0.5 + 0.5;",
"light = light*light;",
"highp float cutoff = 0.90; highp float edge = 0.3;",
"highp float spot = dot(spotDir, lightDir);",
"spot = spot < cutoff ? smoothstep(cutoff*(1.-edge), cutoff, spot) : 1.0;",
"light = light * spot;",
"light = light / (1.0 * 1.0 + lightDist * 0.00000001 + lightDist * lightDist * 0.000001);",
"light = light < 0.05 ? 0.05 : light;",
"outColor = vec4(light * tex.xyz, tex.a);",
"gl_FragDepth = (outColor.a == 0.0 ? 1.0 : gl_FragCoord.z);",
"}"]
.join("\n")