Problems with emulation of N64 graphics
sub-system using PC hardware rendering induced by the difference between N64
and PC graphics hardware capabilities. Many things in N64 work very different
from PC approach. Twelve years ago the situation was much worse: some N64
features were impossible to reproduce on PC hardware; others required complex
code, which also had to take into account capabilities of user's video card and
these capabilities varies greatly. Now every modern card supports shader
programs, and using shaders we can program N64 most difficult to emulate
features.
One of complex tasks was emulation of N64
color combiner. The color combiner equation itself is quite simple and the
formula is the same for all combine modes. Nevertheless, the problem was
complex. The combine equation could not be completely reproduced with standard
Glide3x API as well as with standard OpenGL of that time (1998-2001). Thus,
many N64 combine modes could not be reproduced correctly on PC, only an
approximation was possible, with lost of color information.
OpenGL extensions introduced with GeForce
cards helped to reduce the problem. Glide API also got new extensions after
Voodoo4/5 release. In particular, Glide3x combiner extension was more flexible
than N64 combiner and with it I was able to reproduce N64 combiners 100%
correct … but not always. The main problem is the variety of color inputs,
which N64 combiner can substitute to the equation. N64 can use in the same
equation two standard constant color input, plus some additional parameters can
be used as another constant color input, plus noise plus fractional part of
per-pixel calculated LOD plus vertex color plus textures color. And besides
that N64 often uses two cycle rendering when combiner consists of two equations
and the second one uses output color from the first one as its input. Glide API
has only one constant color (and another one can be used in texture combiner
with an API extension). Two cycles mode can be reproduced if the first cycle
combines textures and the second cycle blends output of the first cycle with
non-texture color inputs. But crafty N64 programmers seldom used good to
emulate combine modes. Most of time they used several constant color inputs and
blend them with texture and vertex colors in both cycles. That made automatic
translation of N64 combiners to Glide3x ones impossible. I had to use various
tricks like pre-calculation of constant expressions. For each combine mode I
had to think, how it can be reproduced. Thus, each combine mode in
Glide64 is manually implemented. Circa ten thousands of modes, several
thousands of corresponding static functions, 16665 lines of code. Titanic work.
Sisyphean toil, as some modes I could not reproduce 100% correct despite of all
my contrivances.
So, the first goal I wanted to achieve in
the new plugin was automatic translator of combine modes. That is where my
acquaintance with OpenGL Shading Language or GLSL began. With GLSL the task
proved to be not just simpler, it became easy. I don't need vertex
transformation, so vertex shader is trivial. Fragment shader can have as many
external constant parameters as necessary, so there was no problem to pass all
necessary constant color inputs to it. Combiner equation is always the same, so
I just parsed its inputs and substitute them to the body of fragment shader.
Initial implementation was circa 300 lines of useful code. 300 lines for
universal translator, which automatically produces 100% correct combiners.
Compare with 16665 lines of code in Glide64.
Initial implementation of combiner
emulation put the project on the same level with Glide64: all combine modes are
supported, but not all color inputs are emulated. In particular, LOD fraction
and noise emulation is missing. LOD calculation is complex task by itself.
Glide64 has only rough software approximation of that parameter. Noise by itself is not complex, but Glide API
does not support random color, and I did not find a good way to implement it.
To my surprise, GLSL also does not have a function which generates random
output, at least at the moment. From the other side, the language is powerful
enough to implement such a function. Quick dig in the Internet led me to the webgl-noise project. This is
an open-source project with variety of GLSL implementations of noise function.
I added one to my project. Now I have a tool to implement noise input as well
as randomized color and alpha dithering. My combiner implementation became more
complex: 400 lines of code.
The screen shots below illustrate how it
works:
Super Smash Bros. Character selection screen: noise is used for unknown characters icons. |
Zelda Majora's Mask: noise in icons |
http://www.opengl.org/sdk/docs/manglsl/xhtml/noise.xml
ReplyDeleteisn't that specifically what you need?
Fwiw, alternatively you could've generated a "noise" texture on the CPU and read that from the shader whenever a random value is requested. That's obviously a less elegant solution, but it theoretically allows you to implement the same noise generation code that the N64 uses. On the other hand, it's likely not going to work well if you need more than one noise value per pixel unless you use atomic counters :|
Yes, noise1 function is exactly what I need, thanks!
DeleteAs I know, these noise* functions are not always supported, and developers prefer to use their own noise implementation. I did not checked, is it supported by my drivers though.
webgl-noise project also has an algorithm, based on a "noise" texture, but I prefer texture-less variant.
Btw, do you know which noise generation code N64 uses? Where can I read about it?
I'm just a Dolphin dev, no idea about N64 hardware details :) Your best bet would be looking for patent texts that describe it (aka mention a specific algorithm which likely is the one used by N64), I guess.
DeleteAnyway, glad I could help. Looking forward to your next blog entries :)
And apparently blogspot fails at keeping up with name changes. Needless to say I'm the same person as the OP.
Delete