sp614x / optifine

1.81k stars 418 forks source link

gbuffers_skybasic uses sun/moon/up positions from previous frame #384

Open Builderb0y opened 7 years ago

Builderb0y commented 7 years ago

Here's some simple code that demonstrates this bug: (for gbuffers_skybasic.fsh, .vsh file is not required). Set the max framerate to 5 to make the issue more noticeable.

#version 120

uniform float viewHeight;
uniform float viewWidth;
uniform mat4 gbufferModelView;
uniform mat4 gbufferProjectionInverse;
uniform vec3 moonPosition;
uniform vec3 sunPosition;
uniform vec3 upPosition;

void main() {
    vec4 screenPos = vec4(gl_FragCoord.xy / vec2(viewWidth, viewHeight) * 2.0 - 1.0, 1.0, 1.0);
    vec3 pos = normalize((gbufferProjectionInverse * screenPos).xyz);

    //demo 1: using uniform-provided up position
    //vec3 upPosNorm = normalize(upPosition);

    //demo 2: using manually-calculated up position
    //vec3 upPosNorm = normalize((gbufferModelView * vec4(0.0, 1.0, 0.0, 0.0)).xyz);

    float posdotup = dot(pos, upPosNorm);

/* DRAWBUFFERS:0 */
    gl_FragData[0] = vec4(vec3(fract(posdotup)), 1.0);
}

Un-comment one of the demos, and you'll see that demo 1 will make the horizon line jump up and down relative to the terrain when quickly rotating the camera up or down. Demo 2 on the other hand keeps the horizon where it should be. It's a decent workaround for upPosition, since it's always at (0.0, 1.0, 0.0) before being rotated by the model view matrix. However, this is not true for sunPosition or moonPosition, which also use values from the previous frame.

Using version HD_U_D4 for MC1.10.2.

sp614x commented 7 years ago

Updated preview B3 with a bugfix for "upPosition". The sun, moon and shadow light positions are updated when applying the celestial rotation. This happens after rendering the basic sky with "gbuffers_skybasic" and before rendering the sun/moon with "gbuffers_skytextured".

  1. Update upPosition
  2. Render basic sky
  3. Apply celestial rotation
  4. Update sunPosition, moonPosition and shadowLightPosition
  5. Render sun and moon (textured sky)
Builderb0y commented 7 years ago

I'm assuming it's not possible to change that order such that celestial rotation is calculated before skybasic rendering?

sp614x commented 7 years ago

It could be pre-calculated, but it will be more complex:

Are the sun, moon and shadow light positions really needed in gbuffers_skybasic?

Builderb0y commented 7 years ago

I personally use skybasic for cloud rendering, which makes the issue very noticeable on low frame rates. Still, it's on my todo list to move that to composite instead, since there's no need to render clouds when you're not looking at them. Also, why does celestial rotation need to be un-rotated for skybasic rendering anyway?

sp614x commented 7 years ago

If the celestial rotation is applied before basic sky rendering, then the basic sky will rotate together with the sun and moon.

Builderb0y commented 7 years ago

I have now re-written most of my sky rendering code to run in composite (minus the clouds and auroras, which are still work in progress), and I'm now in the interesting position that the "top" half of the sky is rendered in skybasic, but the bottom half is rendered in composite. This is done so that stars/custom skyboxes will still render normally in the top half, but the bottom half gets overwritten by my infinite oceans. This works quite well, except during sunset, because the sun position is used to calculate sunset colors. As such, moving your head left or right quickly causes the 2 halves to "split up". So yes, I'd say that having an accurate sun position in the skybasic stage is still needed (if reasonable). If not reasonable, I'll see if I can figure out how to calculate it manually like I did for upPosition.

Builderb0y commented 7 years ago

I've now gone ahead and calculated it manually. Partially to maintain compatibility with older versions of optifine, but mostly because I'm tired of waiting for this issue to be fixed. For anyone else who happens to need accurate sun/moon/up positions in the skybasic stage:

uniform int worldTime;
uniform mat4 gbufferModelView;

varying vec3 sunPosNorm;
varying vec3 upPosNorm;

const float sunRotation = radians(30.0); //sunPathRotation converted to radians. Must match the value set in the .fsh stage!
const vec2 sunData = vec2(cos(sunRotation), -sin(sunRotation)); //don't need an entire matrix to rotate the sun position since it's z position is always 0 before being rotated, and its x position doesn't actually change after being rotated.

void main() {
    gl_Position = ftransform();

    //minecraft's native calculateCelestialAngle() function, optimized and ported to GLSL.
    float ang = fract(worldTime / 24000.0 - 0.25);
    ang = (ang + (cos(ang * 3.14159265358979) * -0.5 + 0.5 - ang) / 3.0) * 6.28318530717959; //0-2pi, rolls over from 2pi to 0 at noon.

    sunPosNorm = normalize((gbufferModelView * vec4(vec3(-sin(ang), cos(ang) * sunData) * 100.0, 1.0)).xyz); //sunData * cos(ang) is the result of simplifying the rotation matrix by removing values that don't actually matter.
    upPosNorm = gbufferModelView[1].xyz;
}

Also, 2 things I noticed while figuring this out: 1: Setting sunPathRotation in the vertex stage doesn't seem to do anything, it must be set in the fragment stage in order to actually change the sun's rotation angle. 2: The sunAngle uniform seems to always be set to 0.0. Are either of these things intended?

sp614x commented 7 years ago

The "sunPathRotation" is only checked in the fragment shader: reference The "sunAngle" value should be valid, it is calculated as:

celestialAngle = mc.world.getCelestialAngle(partialTicks);
sunAngle = (celestialAngle < 0.75f) ? celestialAngle + 0.25f : celestialAngle - 0.75f;
float angle = celestialAngle * (-360.0f);
float angleInterval = shadowAngleInterval > 0.0f ? (angle % shadowAngleInterval - (shadowAngleInterval * 0.5f)) : 0.0f;
if (sunAngle <= 0.5)
{
  // day time
  glRotatef(angle - angleInterval, 0.0f, 0.0f, 1.0f);
  glRotatef(sunPathRotation, 1.0f, 0.0f, 0.0f); //rotate
  shadowAngle = sunAngle;
}
else
{
  // night time
  glRotatef(angle + 180.0f - angleInterval, 0.0f, 0.0f, 1.0f);
  glRotatef(sunPathRotation, 1.0f, 0.0f, 0.0f); //rotate
  shadowAngle = sunAngle - 0.5f;
}
...
setProgramUniform1f("sunAngle", sunAngle);
Builderb0y commented 7 years ago

The "sunPathRotation" is only checked in the fragment shader: reference

Right, I must have missed that being fragment-only. Set that value as soon as I discovered it and never bothered looking it up again after that. Still though, is there a specific reason it's only checked in the fragment stage?

The "sunAngle" value should be valid, it is calculated as:

Well, it's still broken for me. Tested in final, composite, terrain, entities, and skybasic. gl_FragData[0] = vec4(sunAngle, fract(sunAngle), abs(sunAngle), 1.0); All of them seem to return 0.0, regardless of the time.

sp614x commented 7 years ago

Most options are checked only in the fragment shaders. The option parser is relatively simple. The "sunAngle" is only updated when a shadow map is defined, probably a bug.