New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[1.20]: Implement OIT Rendering. #9607
base: 1.20.x
Are you sure you want to change the base?
Conversation
Does this fix sorting w/ lightning + water/whatever? What is preventing this from working for Creepers/can that be easily fixed? And how much will this break mods doing custom stuff (not sure exactly what that may be)? |
Yes, as far as I can see, it fixes the combination with Lightning + Water. |
1e4f576
to
3d35fd1
Compare
+ private final String importPatternPrefix; | ||
+ | ||
+ public GlslPreprocessor() | ||
+ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bracket should be on previous line
+ } | ||
+ | ||
+ public GlslPreprocessor(String importPatternPrefix) | ||
+ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bracket should be on previous line
+ } | ||
+ | ||
+ public List<String> process(String p_166462_, com.mojang.blaze3d.shaders.Program.Type type) | ||
+ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bracket should be on previous line and this method should potentially be added to the .exc
file? I believe that only affects parameters so I don't believe (?) it should cause issues with it overriding the method in IForgeGlslPreprocessor
. Also might as well include the override annotation
+ | ||
+ public GlslPreprocessor(String importPatternPrefix) | ||
+ { | ||
+ this.importPattern = Pattern.compile("(#(?:/\\*(?:[^*]|\\*+[^*/])*\\*+/|\\h)*%s(?:/\\*(?:[^*]|\\*+[^*/])*\\*+/|\\h)*(?:\"(.*)\"|<(.*)>))".formatted(importPatternPrefix)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth a comment above this line along the lines of: Forge: Copy of REGEX_MOJ_IMPORT with support for custom prefixes
just so that if mojang changes their regex at some point it is easier to know what part of this has to be updated.
|
||
- public static Program m_166604_(Program.Type p_166605_, String p_166606_, InputStream p_166607_, String p_166608_, GlslPreprocessor p_166609_) throws IOException { | ||
+ public static Program m_166604_(Program.Type p_166605_, String p_166606_, InputStream p_166607_, String p_166608_, GlslPreprocessor p_166609_) throws IOException | ||
+ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bracket should be on previous line
/** | ||
* Defines a render call that can be queued up, based on a particle render type. | ||
*/ | ||
private interface IParticleRenderCall extends IRenderCall { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bracket should be on a new line
* @param modelViewMatrix The model view matrix to use. | ||
* @param projectionMatrix The projection matrix to use. | ||
*/ | ||
private record RenderedBufferRenderCall(RenderType renderType, BufferBuilder.RenderedBuffer renderedBuffer, Matrix4f modelViewMatrix, Matrix4f projectionMatrix) implements IRenderTypeBasedRenderCall { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bracket should be on a new line
* @param cameraZ The camera Z position. | ||
* @param projectionMatrix The projection matrix to use. | ||
*/ | ||
private record ChunkGeometryRenderCall(RenderType renderType, PoseStack currentPoseStack, double cameraX, double cameraY, double cameraZ, Matrix4f projectionMatrix) implements IRenderTypeBasedRenderCall { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bracket should be on a new line
@Override | ||
public void drawDirect() | ||
{ | ||
renderType.doRender(renderedBuffer, (shader) -> {}, (shader) -> {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given how both of these are NO-OP ShaderInstance
consumers and ChunkGeometryRenderCall
and ParticleRenderCall
also both have NO-OP ShaderInstance
consumers we should just create and store a single private static final Consumer<ShaderInstance> NO_OP_CONSUMER = shader -> {};
in the OITLevelRenderer
and then reference that in all these places so that we only allocate a single object for all these consumers
* @param camera The camera to use. | ||
* @param partialTickTime The partial tick time to use. | ||
*/ | ||
private record ParticleRenderCall(ParticleRenderType particleRenderType, Iterable<Particle> toRender, @Nullable Frustum clippingHelper, Camera camera, float partialTickTime) implements IParticleRenderCall { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bracket should be on a new line
@@ -25,7 +25,7 @@ net/minecraft/client/renderer/block/model/ItemTransform.<init>(Lorg/joml/Vector3 | |||
net/minecraft/client/renderer/block/model/ItemTransforms.<init>(Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lcom/google/common/collect/ImmutableMap;)V=|p_111798_,p_111799_,p_111800_,p_111801_,p_111802_,p_111803_,p_111804_,p_111805_,moddedTransforms | |||
net/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$ChunkCompileTask.<init>(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;Lnet/minecraft/world/level/ChunkPos;DZ)V=|p_194422_,pos,p_194423_,p_194424_ | |||
net/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$RebuildTask.<init>(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;Lnet/minecraft/world/level/ChunkPos;DLnet/minecraft/client/renderer/chunk/RenderChunkRegion;Z)V=|p_194426_,pos,p_194427_,p_194428_,p_194429_ | |||
net/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$ResortTransparencyTask.<init>(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;Lnet/minecraft/world/level/ChunkPos;DLnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$CompiledChunk;)V=|p_112888_,pos,p_112889_,p_112890_ | |||
net/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$ResortTransparencyTask.<init>(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;Lnet/minecraft/world/level/ChunkPos;DLnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$CompiledChunk;)V=|p_112888_,renderType,pos,p_112889_,p_112890_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signature is missing the added RenderType
parameter
I've played around with this and found a few issues with it:
This was tested on commit 3d35fd1 and all points except the last one were reproduced in a plain Forgedev environment |
As perhaps a useful prior work, we also attempted to implement the WBOIT algorithm in CaffeineMC/sodium-fabric#519, but ran into a lot of issues with the approach. The algorithm doesn't handled layered translucency very well, and fails badly once you start mixing different colors (see some of the linked screenshots in the thread.) My understanding is that the algorithm (as we implemented it) was correct and that our visual artifacts were within expectations of the original paper. We did a lot of tweaking with the weighting formula but didn't manage to produce results that were any better. If you do want to try and go ahead with this approach, you may find better results with Moment-Based Order Independent Translucency (MBOIT). The performance isn't nearly as "good"-- though probably still much better than trying to sort the geometry on the CPU-- and it appears to handle these kinds of layered effects much better. It also shares many similarities with the original WBOIT paper, so your work here would prove a very useful platform for implementing the MBOIT paper. |
|
@marchermans, this pull request has conflicts, please resolve them for this PR to move forward. |
cb455e6
to
2d5ba40
Compare
Order Independent Transparent Rendering
OIT Rendering, for short OIT, is a rendering mechanic which allows rendering several transparent objects without sorting their order first.
Existing mechanic
If we take a look at the existing rendering mechanics within Minecraft, then Mojang uses two major systems to handle layering transparent surfaces:
This works virtually all of the time. But not in all:
For example:
Expand to see examples of particles
As you can see, the smoke particles are semi-transparent, but the effect cloud particles in the background are not visible.
Expand to see examples of shulker bullet halos
As you can see, the entity renderer for the shulker and the clouds in the background are not rendered properly.
Expand to see examples of ice block interactions
Visible here is the main example of the rendering engine breaking transparency since the water is not visible behind the ice.
Impact
These are corner cases in vanilla, and the priority of fixing them is lower. However, these issues happen regularly in a modded environment when rendering additional components into the world. Examples of cases where this happens are custom particles, schematic previews, fake worlds, etc.
New mechanic
This new feature implements weighted order independent rendering based on the paper published by: McGuire and Bavoil1. The idea here is to capture all rendering calls which involve transparent subsections that we are interested in and draw them to a separate system.
This system emulates the usual ALPHA <-> 1-ALPHA rendering, but more on the restrictions of this new system later.
Implementation:
This new system uses additional hooks placed into the rendering pipelines for the level, particles, and CPU Bound computed buffers to check whether the
RenderType
orParticleRenderType
will render transparent components. If so, it will capture the call and all related contextual information needed to retrigger and store it. Once the world has been rendered, it binds a new custom render target used to perform the OIT Rendering of the captured calls. Using a new system to adapt shaders, the system will calculate the transparent processing that normally happens when you perform an ALPHA <-> 1-ALPHA blended rendering on the GPU, but this time in the shader.Afterward, the resulting color and background coverage, known as the reveal, is used for blitting the transparent rendered output to the main buffer.
Results
Expand to see examples of particles
As you can see, the smoke particles are semi-transparent and the effect cloud particles behind them are now visible.
Expand to see examples of shulker bullet halos
As you can see, the backgrounds behind the shulker halo are rendered properly and are visible.
Expand to see examples of ice block interactions
Visible here is the main example of the rendering engine working properly, the water is visible behind the ice.
New Hooks and Events
The original PoC for this PR implemented the hooks raw into the OIT subsystem. However, I always envisioned that this system runs on top of several hooks for the community.
These hooks or events are the following:
RegisterGlslPreprocessorsEvent
: Event fired when the preprocessors for an event are determined. Allows modders to register custom preprocessors that modify shaders. Used by Forges OIT system to wrap the shader execution and optionally handle the OIT render pass. Additional adaptations were made toRenderTargetEvent.Create
andRenderTargetEvent.Resize
: Allows for managing customRenderTarget
-instances and implementations, which shaders can use. Forges OIT system uses a customRenderTarget
implementation with different FrameBuffers and attached textures to handle the Transparency calculation.ShaderEvent.OnAttachmentInitialization
fired when the shader instances are attached to GPU programs. Allows for the configuration of custom attributes when shaders are adapted.ParticleRenderType#requiresTransparencySorting
to indicate to external systems is this particle render type uses transparency components and requires sorting. Setting this totrue
will allow the render type to be transformed by OIT.Current problems:
Status:
The PR enables OIT rendering by default for now.
This is to gather feedback on the feature.
Currently, the shaders use an ARB extension for OpenGL, which is supposedly supported on all platforms Mojang runs on. Including Mac OSX; however, the actual impact still needs to be seen.
Performance impact:
By default, this has no negative impact.
However, depending on your environment, you can expect 10 to 15% FPS improvements (the more transparency-related rendering is needed for a scene, the bigger the impact, so oceans/beaches have a much larger impact than desserts). Besides this, the frame times get more stable, given that now no sorting needs to happen based on your position.
Footnotes
Weighted Blended Order-Independent Transparency ↩