gonetz / GLideN64

A new generation, open-source graphics plugin for N64 emulators.
Other
754 stars 175 forks source link

z-clipping issue (GLES) #588

Open Gillou68310 opened 9 years ago

Gillou68310 commented 9 years ago

Here's a hack to fix the missing heart in both zelda games: https://github.com/Gillou68310/GLideN64/commit/1ddecf779935c8d7d9a3200074b283feccdf409f

Weird thing is this hack is only necessary for GLES versions (GLES2 and GLES3, I couldn't check GLES3.1) the desktop version doesn't seems to need it. It might be interesting to understand why this is working fine on desktop.

purplemarshmallow commented 9 years ago

the problem is that clipping does not work properly for GLES, the heart and the map arrows are clipped away. Adding a hack is a bad idea, it needs to be fixed properly because other games are affected as well. The characters and vehicles in Star Wars Ep1 racer are cut off. The desktop version does not have these problems.

Gillou68310 commented 9 years ago

Adding a hack is a bad idea, it needs to be fixed properly because other games are affected as well.

I totally agree, I just provided the hack as a possible temporary solution ;-)

gonetz commented 9 years ago

@purplemarshmallow - could you explain why do you think that it is clipping and why these particular polygons are clipped?

purplemarshmallow commented 9 years ago

I think it's the same problem as in rice video Rice Video has this comment:

// Hack needed to show flashing last heart and map arrows in Zelda OoT & MM
// It renders at Z cordinate = 0.0f that gets clipped away
// So we translate them a bit along Z to make them stick

gonetz commented 9 years ago

I remember similar problem on desktop too. Polygon may not use depth compare at all, as this heart rectangle, but if it has negative z it will be clipped. I tried to force z to zero, but it causes problems with fog. So, I just disabled z-clipping for ucodes, which do not use near-far clipping. Desktop GL allows to do that. GL ES does not. If it clip polygons with Z==0 it's bad. Glide64 uses software clipping and culling, so this problem resolved on plugin's side. It was done because Glide3x API does not provide polygons clipping. I don't want to make custom clipping when I have built in GL one. From the other side, the only alternative for GL ES is a hack. @Gillou68310 - as I understand, your patch does the same trick - translates vertices a bit along Z, right?

Gillou68310 commented 9 years ago

Exact.

Gillou68310 commented 9 years ago

Here's the value to fix duke nukem zero hour: gSP.matrix.projection[3][2] += 1.f;

dukenok dukeok

ghost commented 9 years ago

@Gillou68310 Wouldn't it be possible to add the depth bias after the projection matrix is applied to the vertex (somewhere in gsPProcesVertex(), I think), and in a way so that this bias is independent of the y coordinate?

It would involve changing the values of the near and far plane, but if no native clipping has to be done this would not affect it negatively.

The good thing would be that if the bias is chosen wisely, it would be quite a legit approach.

Gillou68310 commented 9 years ago

@Gillou68310 Wouldn't it be possible to add the depth bias after the projection matrix is applied to the vertex (somewhere in gsPProcesVertex(), I think), and in a way so that this bias is independent of the y coordinate?

Sure putting vtx.z += 0.5f; in gsPProcesVertex() produces the same effect (zelda) ;-)

It would involve changing the values of the near and far plane, but if no native clipping has to be done this would not affect it negatively.

I'm not sure to understand why this would be necessary?

ghost commented 9 years ago

I'm sure sure to understand why this would be necessary?

I did not explain myself very clearly. What I meant is that an element in the near plane would now have z-coordinate different to 0 (or whatever he is intended to have). Z-compare effects would have problems if comparing something transformed to something not transformed.

I believe the equivalent to your code would be vtx.z += 0.5f*vtx.y . I believe it would be possible to find values m,n such that, vtx.z = (vtx.z+m)/n would give correct graphics. In your case it also does assuming 0.5*vtx.y is positive and big enough.

Gillou68310 commented 9 years ago

I did not explain myself very clearly. What I meant is that an element in the near plane would now have z-coordinate different to 0 (or whatever he is intended to have). Z-compare effects would have problems if comparing something transformed to something not transformed.

It makes sense, thanks for the clarifications, however I'm not sure how we should deal with this.

I believe it would be possible to find values m,n such that, vtx.z = (vtx.z+m)/n would give correct graphics

You mean "m" and "n" being 2 constants compatible with all games?

BTW setting vtx.z += 4.f; fixes the z-clipping issue in starwars ep1, while I was not able to fix it via the projection matrix.

ghost commented 9 years ago

You mean "m" and "n" being 2 constants compatible with all games?

Yes I meant that. m should be big enough to guarantee that vtx.z+m is positive, but not so big as to create a rounding error in OGL floating point format. And n can be any number (a power of 2 for simplicity) to make sure final values are within the OGL standards.

The numbers depend on the fixed point format of the N64 and the floating point format of OpenGL. I will think about them this night, and see if I can come up with them.

Gillou68310 commented 9 years ago

Ok I let you do the math, I'm not good at it anyway ;-)

ghost commented 9 years ago

Mmm so N64 uses 32 bit fixed point. I was expecting less. That can not be perfectly approximated with floats, and will make the this approach (actually any hardware accelerated approach) error prone.

Try m=32768, n=2; m=128, n=257/256;

Edit: Changed values. I also noticed that gspF3DNoN only disables near clipping. Therefore, the previous approach will change far clipping. In order to maintain far clipping correct, the coordinate of the far plane should be maintained at the same place. The formula doing so would be, m = z_far*(n-1) Is z_far known?

Gillou68310 commented 9 years ago

Here's another hack to fix both mario 64 and mario kart 64 background (blue sky) with widescreen hack enable:

if(gSP.matrix.projection[3][2] == -1.f) vtx.w *= ogl.getAdjustScale();

mknok mkok

@standard-two-simplex I not sure to understand how to implement your method :-S

ghost commented 9 years ago

Add a line vtx.z = (vtx.z + 32768.f)/2.f like in the example you gave before.

BTW setting vtx.z += 4.f; fixes the z-clipping issue in starwars ep1, while I was not able to fix it via the projection matrix.

I think it will screw the backgrounds though.

gonetz commented 9 years ago

Is z_far known?

Yes. It is set by viewport command. z_max = view_trans[2] + view_scale[2]

Gillou68310 commented 9 years ago

Add a line vtx.z = (vtx.z + 32768.f)/2.f like in the example you gave before.

It doesn't looks good: sans titre

I think it will screw the backgrounds though.

Hum the background looks good to me.

gonetz commented 9 years ago

Here's another hack to fix both mario 64 and mario kart 64 background (blue sky) with widescreen hack enable:

A hack for the hack :) Well, why not?

Gillou68310 commented 9 years ago

:-)

ghost commented 9 years ago

I might have pushed everything behind the far plane. vtx.z = (vtx.z + gSP.viewport.farz)/2f;

Gillou68310 commented 9 years ago

@standard-two-simplex sorry for the delay, the new equation is a lot better! No more missing heart on zelda but no more fog also :-( Same for episode 1 racer. But we're definetly getting closer ;-)

gonetz commented 9 years ago

No more missing heart on zelda but no more fog also :-(

Manipulations with z will also break depth buffer based effects, e.g. coronas in zelda. Probably it can be solved by applying reverse transformation to buffer z.

ghost commented 9 years ago

I had not thought about fog, but it will screw the z compare effects too, and lighting and other effects possibly. It would require modifying everything, and since it is a hacky approach, better not to do it.

@gonetz What about changing the viewport instead? Changing the value of nearz.

Probably it can be solved by applying reverse transformation to buffer z.

Also what do you mean by that?

gonetz commented 9 years ago

What about changing the viewport instead? Changing the value of nearz.

Do you know how to do it with GLES?

Also what do you mean by that?

vtx.z is original vertex z. We apply a transformation to it before sending it to video card, e.g.

vtx.z = (vtx.z + gSP.viewport.farz)/2f;

To emulate depth buffer effect, plugin reads depth buffer from video card. We know that depth value is transformed, so we apply reverse transformation to read values:

z = 2*z - gSP.viewport.farz;

ghost commented 9 years ago

Do you know how to do it with GLES?

No, I don't know about GL. I was wondering if changing the value of the variable nearz could help.

To emulate depth buffer effect, plugin reads depth buffer from video card. We know that depth value is transformed, so we apply reverse transformation to read values:

I see. Thanks for the explanation.

gonetz commented 9 years ago

No, I don't know about GL. I was wondering if changing the value of the variable nearz could help.

No. nearz/farz not used for vertex z calculation.

gonetz commented 9 years ago

@Gillou68310 - please try this patch: https://drive.google.com/file/d/0B0YqMPjGo3B2c1VhQ3NESWp0WWc/view?usp=sharing

I tested that it fixes missing heart problem. Please check: does it work for other issues?

Gillou68310 commented 9 years ago

Just tried, both zelda games are perfect but duke nukem zero hour + star wars episode 1 still have the issue :-(

Gillou68310 commented 9 years ago

I found this while googling http://stackoverflow.com/questions/5960757/how-to-emulate-gl-depth-clamp-nv

It will not work for GLES2 (gl_FragDepth not writable) but maybe it's worth giving it a try on GLES3.x

Gillou68310 commented 9 years ago

Removing the (gSP.geometryMode & G_ZBUFFER) == 0 condition fixes the z-clipping issue for all games but of course some triangles now have a depth of 0 were they shouldn't.

Gillou68310 commented 9 years ago

Replacing ```(gSP.geometryMode & G_ZBUFFER) == 0 by ((gDP).otherMode).depthCompare == 0 fixes duke nukem + zelda. Star wars still have issues.

gonetz commented 9 years ago

What kind of problem is in Star wars? My hack may fix only polygons which look like sprites.

I investigated only missing heart problem. Missing polygon has vertices with z = -1.3 w = 1.0. Thus, z/w < -1 and polygon z-clipped. Zelda ucode is NoN, that is it does not use near clipping. So, polygon is not clipped by original hardware. GLideN64 set z-clipping off on desktop, so desktop version works too. GL ES does not support z-clipping disabling. Thus, we can't solve all problems with z-clipping on mobile platforms. IMO, only sprite-looking polygons can be solved without breaking anything else, and only for case when z-buffer is not used at all during polygon draw. (gSP.geometryMode & G_ZBUFFER) == 0 condition ensures it. In that case we can set any z, which ensure that vertex will be between near-far clipping planes. My GBI.isNoN() condition is probably redundant. At least, my current code always set z-clipping off when depth buffer is not used.

Gillou68310 commented 9 years ago

What kind of problem is in Star wars? My hack may fix only polygons which look like sprites.

It makes sense because in episode 1 racer z-clipping issue is on non sprites polygons. However on duke nukem, z-clipping issue is on sprite polygons but it is not fixed with your hack.

sans titre sans titre2

gonetz commented 9 years ago

Star wars is an example of the near clipping, which can't be fixed with harmless hacks. Duke Nukem can be fixed, but my (gSP.geometryMode & G_ZBUFFER) == 0 condition is too strong. This one is enough: if (GBI.isNoN() && gDP.otherMode.depthCompare == 0 && gDP.otherMode.depthUpdate == 0)

Gillou68310 commented 9 years ago

Ok so let's merge this hack. If star wars is proved to not use depth buffer based effects we can always use the vtx.z += 4.f hack just for this game. Hopefully no other games are affected by z-clipping issue.

gonetz commented 9 years ago

Ok.

purplemarshmallow commented 9 years ago

If star wars is proved to not use depth buffer based effects we can always use the vtx.z += 4.f hack just for this game.

469 Star Wars Episode I - Racer unfortunately uses depth buffer based effects

Gillou68310 commented 9 years ago

Too bad :-(

CornN64 commented 8 years ago

The Zelda missing hart hack originated from DaedalusX64. I came up with this hack since the Play station portable(PSP) just like GLES has issues with polygon clipping and I figured that I could just modify the matrix transform slightly to make the hart (and map arrows) end up inside the NDC box and avoid the clipping. Anyways Gonetz hack is more elegant solution for "billboards" if it can be made to work. But it will never work for true geometry. N64 used a strange way to handle near field geometry and could turn off near field clipping to which is why Star Wars Episode I looks like it does when NDC clipping is turned on.

gonetz commented 8 years ago

Anyways Gonetz hack is more elegant solution for "billboards" if it can be made to work. But it will never work for true geometry.

True. I mentioned it in my description for this hack.

loganmc10 commented 6 years ago

The issue in Star Wars Ep I Racer is still there, can't see the vehicle you're driving, only the rockets.

Tested on my phone (Mali T860-MP2, I think it supports GLES3.2)

gonetz commented 6 years ago

Near plane clipping not supported on GLES devices.

fzurita commented 6 years ago

Glide64mk2 was able to get around the lack of ability to disable near plane clipping. I'm not quite sure how yet though.

gonetz commented 6 years ago

Glide64 does software polygons clipping.

fzurita commented 2 years ago

Should this be closed?