Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
renderpass [2019/04/12 13:16] – [Multiple Rendering-Targets] admin | renderpass [Unknown date] (current) – removed - external edit (Unknown date) 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Renderpasses ====== | ||
- | In the simplest rendering pipeline you have one rendering loop, where all scene elements are drawn. | ||
- | But in many cases you need additional steps before or after this main loop to produce the visual result you want. | ||
- | An example is a shadow mapping pre-rendering or a post-rendering blur effect. | ||
- | |||
- | With Gorilla3D it is possible to easily build your own render pass and integrate it in your rendering pipeline. You can choose between 3 different moments of rendering: | ||
- | |||
- | ^ Type ^ Description ^ | ||
- | | TRenderPassType.Pre | render-pass will be executed before main loop. | | ||
- | | TRenderPassType.Intermediate | render-pass will be executed in between main loop (very ineffective - because executed each renderlist element) | | ||
- | | TRenderPassType.Post | render-pass will be executed after main loop. | | ||
- | |||
- | ===== Rendering Method ===== | ||
- | |||
- | You are allowed to configure a destination / method for your individual render pass. | ||
- | |||
- | ^ Type ^ Description ^ | ||
- | | TRenderPassMethod.RenderList | It will execute the render list. If exists, it will apply the render pass material to all 3D objects. Otherwise it will use the element specific material shaders. | | ||
- | | TRenderPassMethod.ToContext | The render pass will work on the current context texture (useful for post-effects) with the render-pass material shader. | | ||
- | |||
- | In the next step you have to decide, if the element materials (only in RenderList) or an especially for the render-pass created material should be used. | ||
- | Because sometimes you want the full scene just to be rendered from another perspective (like reflection) or you want to store a specific value (like the depth-value in shadow mapping) in the FBO. | ||
- | |||
- | |||
- | ===== Framebuffer-Object (FBO) ===== | ||
- | Each render pass sets up a default framebuffer object (FFBO) with a default output texture (FFBOTexture), | ||
- | |||
- | __Caution: | ||
- | |||
- | To change the FBO rendering target(s) simply overwrite the protected DoSetupTexturesByViewport() method, like this: | ||
- | |||
- | <file pascal> | ||
- | uses | ||
- | Gorilla.Context.Texturing; | ||
- | | ||
- | procedure TMyRenderPass.DoSetupTexturesByViewport(const AContext : TContext3D; | ||
- | const AWidth, AHeight : Integer); | ||
- | var LSize : TPoint; | ||
- | LUpdateTex : Boolean; | ||
- | begin | ||
- | // update FBO texture size | ||
- | LSize := ComputeRenderingSize(AContext, | ||
- | |||
- | LUpdateTex := true; | ||
- | if Assigned(FFBOTexture) then | ||
- | begin | ||
- | // is size really different? - if not leave | ||
- | if (FFBOTexture.Width = LSize.X) and (FFBOTexture.Height = LSize.Y) then | ||
- | LUpdateTex := false | ||
- | else | ||
- | begin | ||
- | FFBO.UnattachTexture(FFBOTexture.Texture); | ||
- | FreeAndNil(FFBOTexture); | ||
- | end; | ||
- | end; | ||
- | |||
- | if LUpdateTex then | ||
- | begin | ||
- | FFBOTexture := TGorillaTextureBitmap.Create(LSize.X, | ||
- | FFBOTexture.BeginSetup(); | ||
- | try | ||
- | with FFBOTexture do | ||
- | begin | ||
- | Components := TPixelFormatEx.Depth; | ||
- | Format := TPixelFormatEx.Depth; | ||
- | MinFilter := TTextureFilter.Nearest; | ||
- | MagFilter := TTextureFilter.Nearest; | ||
- | WrapS := TGorillaTextureWrap.ClampToBorder; | ||
- | WrapT := TGorillaTextureWrap.ClampToBorder; | ||
- | end; | ||
- | finally | ||
- | FFBOTexture.EndSetup(); | ||
- | end; | ||
- | | ||
- | // attach the texture to the FBO | ||
- | if Assigned(AContext) and Assigned(FFBO) then | ||
- | FFBO.AttachTexture(AContext, | ||
- | end; | ||
- | end; | ||
- | </ | ||
- | |||
- | Here we changed the FBO texture format to a depth-texture. | ||
- | |||
- | **The default format is RGBA with an unsigned byte for each component (4 Byte per Pixel).** | ||
- | |||
- | ==== Multiple Rendering-Targets ==== | ||
- | |||
- | As said above, we can use multiple render targets since OpenGLES v3. | ||
- | |||
- | Multiple render targets are useful to store values in the shader to different output textures. | ||
- | So you possibly could store the depth, diffuse, specular, normals or alpha values in separated textures. | ||
- | Afterwards you could combine those together in some kind of way. | ||
- | For example this is how deferred rendering works. | ||
- | |||
- | This can be done by creating a TGorillaTextureBitmap and attaching it to the framebuffer object (FBO). | ||
- | |||
- | Gorilla3D provides an extended TClearTarget enumeration type called TRenderTarget, | ||
- | |||
- | <file pascal> | ||
- | TRenderTarget = (Color_0, Depth, Stencil | ||
- | {$IFDEF GL_ES_VERSION_3_0}, | ||
- | Color_1, Color_2, Color_3, Color_4, Color_5, Color_6, Color_7, | ||
- | Color_8, Color_9, Color_10, Color_11, Color_12, Color_13, Color_14, | ||
- | Color_15 | ||
- | {$ENDIF}); | ||
- | </ | ||
- | |||
- | === Attaching multiple targets === | ||
- | To attach a texture to the framebuffer object we call the AttachTexture() method of the FBO and supply the intended rendertarget (one of the values above). | ||
- | |||
- | Caution: Not all GPU's support all render targets. Some GPU's only support up to 8 targets or less. | ||
- | |||
- | <file pascal> | ||
- | if Assigned(AContext) and Assigned(FFBO) then | ||
- | FFBO.AttachTexture(AContext, | ||
- | </ | ||
- | |||
- | === Using multiple targets === | ||
- | |||
- | To use a render target from the framebuffer object, we need to clear it on each rendering step. | ||
- | After that, we have to activate it for shader usage by the Prepare() method. | ||
- | |||
- | We do this all in the render-pass callback method (DoOnRenderPass), | ||
- | |||
- | <file pascal> | ||
- | [...] | ||
- | case AState of | ||
- | TRenderPassEventState.OnPassBegin : | ||
- | begin | ||
- | [...] | ||
- | LTarget := TRenderTarget.Color_1; | ||
- | | ||
- | // clear the specific render target buffer | ||
- | FFBO.Clear(Point(AContext.Width, | ||
- | LTarget, TAlphaColorF.Create(0, | ||
- | | ||
- | // bind again the framebuffer object | ||
- | FFBO.Bind(); | ||
- | | ||
- | // activate the specific render target buffer for shader | ||
- | FFBO.Prepare([LTarget]); | ||
- | end; | ||
- | | ||
- | [...] | ||
- | </ | ||
- | |||
- | On " | ||
- | |||
- | <file pascal> | ||
- | TRenderPassEventState.OnPassEnd : | ||
- | begin | ||
- | [...] | ||
- | | ||
- | // unbind framebuffer object | ||
- | FFBO.Unbind(); | ||
- | end; | ||
- | </ | ||
- | |||
- | === In Shader === | ||
- | |||
- | The usage in your material shader depends on your target setup and the activated targets: | ||
- | Preparing 2 render targets will be used like this: | ||
- | |||
- | <file pascal> | ||
- | FFBO.Prepare([TRenderTarget.Color0, | ||
- | </ | ||
- | |||
- | In Fragment-Shader: | ||
- | <file GLSL> | ||
- | layout(location = 0) out vec4 outColor_0; | ||
- | layout(location = 1) out vec4 outColor_2; | ||
- | layout(location = 2) out vec4 outColor_3; | ||
- | |||
- | [...] | ||
- | |||
- | // output one with red color | ||
- | outColor_0 = vec4(1.0, 0.0, 0.0, 1.0); | ||
- | |||
- | // output two with green color | ||
- | outColor_2 = vec4(0.0, 1.0, 0.0, 1.0); | ||
- | |||
- | // output three with blue color | ||
- | outColor_3 = vec4(0.0, 0.0, 1.0, 1.0); | ||
- | </ | ||
- | ===== Start / End ===== | ||
- | |||
- | The render-pass controller provides a callback event for the start and the end of each rendering cycle. Simply overwrite the protected DoOnRenderPass() method. | ||
- | |||
- | Here you can change the view, projection matrices or re-configure OpenGL context. | ||
- | But do not forget to reset your changes on " | ||
- | |||
- | Here is a short example for a callback function. | ||
- | |||
- | <file pascal> | ||
- | procedure TMyRenderPass.DoOnRenderPass(const AContext : TContext3D; | ||
- | const ACount : Integer; const AState : TRenderPassEventState; | ||
- | var LCtx : TCustomContextOpenGL; | ||
- | begin | ||
- | inherited; | ||
- | |||
- | LCtx := TCustomContextOpenGL(AContext); | ||
- | case AState of | ||
- | OnPassBegin: | ||
- | begin | ||
- | // rendering method for rendering pass | ||
- | FFBO.Clear(Point(AContext.Width, | ||
- | [TRenderTarget.Color_0, | ||
- | TAlphaColorF.Create(0, | ||
- | FFBO.Bind(); | ||
- | FFBO.Prepare([TRenderTarget.Color_0, | ||
- | | ||
- | // deactivate ZTest in OpenGL | ||
- | AContext.SetContextState(TContextState.csZTestOff); | ||
- | | ||
- | // Set view matrix and angle of view | ||
- | AContext.SetCameraMatrix(GetViewMatrix(AContext)); | ||
- | AContext.SetCameraAngleOfView(Viewport.CurrentCamera.AngleOfView); | ||
- | end; | ||
- | |||
- | OnPassEnd: | ||
- | begin | ||
- | FFBO.Unbind(); | ||
- | end; | ||
- | end; | ||
- | end; | ||
- | </ | ||
- | |||
- | ==== Iterations ==== | ||
- | |||
- | The callback also provides an " | ||
- | It is possible to set a number of iterations this pass should run. | ||
- | This is very useful for example for ping-pong rendering techniques like blurring. | ||
- | |||
- | Set the " | ||
- | The DoOnRenderPass() method will be called on each iteration step. | ||
- | |||
- | ==== Helpful methods ==== | ||
- | |||
- | To use a different camera view or change to another projection mode, overwrite the following methods: | ||
- | |||
- | * function GetViewMatrix(const AContext : TContext3D) : TMatrix3D; | ||
- | * function GetProjectionMatrix(const AContext : TContext3D) : TMatrix3D; | ||
- | |||
- | ===== Render-Pass Material ===== | ||
- | |||
- | To create a individual material shader for your render-pass you need to extend **TGorillaRenderPassMaterialSource** and **TGorillaRenderPassMaterial**. | ||
- | |||
- | <file pascal> | ||
- | uses | ||
- | FMX.Types3D, | ||
- | FMX.Materials, | ||
- | FMX.MaterialSources, | ||
- | Gorilla.Controller; | ||
- | | ||
- | TMyRenderPassMaterial = class(TGorillaRenderPassMaterial) | ||
- | protected | ||
- | procedure DoApply(const Context: TContext3D); | ||
- | procedure DoInitialize(); | ||
- | public | ||
- | end; | ||
- | |||
- | TMyRenderPassMaterialSource = class(TGorillaRenderPassMaterialSource) | ||
- | protected | ||
- | function CreateMaterial() : TMaterial; override; | ||
- | public | ||
- | end; | ||
- | </ | ||
- | |||
- | Here you can have a look at this example of a render-pass material implementation. | ||
- | |||
- | Notice: No multiple targets used here. We simple write to default output buffer by gl_FragColor. | ||
- | |||
- | <file pascal> | ||
- | uses | ||
- | Gorilla.Context.GLES; | ||
- | | ||
- | ResourceString SOURCE_VS = | ||
- | ' | ||
- | '' | ||
- | ' | ||
- | '' | ||
- | ' | ||
- | '' | ||
- | 'void main( void ){' + | ||
- | ' | ||
- | ' | ||
- | ' | ||
- | ' | ||
- | |||
- | ResourceString SOURCE_FS = | ||
- | ' | ||
- | '' | ||
- | 'void main( void ){' + | ||
- | ' | ||
- | ' | ||
- | |||
- | procedure TMyRenderPassMaterial.DoInitialize(); | ||
- | var LOGLBytes : TArray< | ||
- | begin | ||
- | LOGLBytes := TEncoding.ASCII.GetBytes(SOURCE_VS); | ||
- | FVertexShader := TShaderManager.RegisterShaderFromData(' | ||
- | TContextShaderSource.Create(TContextShaderArch.GLSL, | ||
- | LOGLBytes, | ||
- | [ | ||
- | TContextShaderVariable.Create(' | ||
- | ] | ||
- | ) | ||
- | ]); | ||
- | |||
- | LOGLBytes := TEncoding.ASCII.GetBytes(SOURCE_FS); | ||
- | FPixelShader := TShaderManager.RegisterShaderFromData(' | ||
- | TContextShaderSource.Create(TContextShaderArch.GLSL, | ||
- | LOGLBytes, | ||
- | [] | ||
- | ) | ||
- | ]); | ||
- | end; | ||
- | |||
- | { TMyRenderPassMaterial } | ||
- | |||
- | procedure TMyRenderPassMaterial.DoApply(const Context: TContext3D); | ||
- | begin | ||
- | // activate shaders | ||
- | Context.SetShaders(FVertexShader, | ||
- | // set model view projection matrix | ||
- | TCustomContextOpenGL(Context).SetShaderVariable(' | ||
- | end; | ||
- | |||
- | { TMyRenderPassMaterialSource } | ||
- | |||
- | function TMyRenderPassMaterialSource.CreateMaterial() : TMaterial; | ||
- | begin | ||
- | Result := TMyRenderPassMaterial.Create(Self); | ||
- | end; | ||
- | </ | ||
- | |||
- | You than have to overwrite the CreateMaterialSource() method and create your material source. | ||
- | In case you'd like to use the element specific material shaders, return nil in the CreateMaterialSource() method. | ||
- | |||
- | <file pascal> | ||
- | function TMyRenderPass.CreateMaterialSource() : TGorillaRenderPassMaterialSource; | ||
- | begin | ||
- | result := TMyRenderPassMaterialSource.Create(Self); | ||
- | end; | ||
- | </ | ||
- | |||
- | ===== How to use ===== | ||
- | |||
- | Using your own render pass is very easy. Just instanciate it and attach the viewport to it. | ||
- | This will automatically integrate your render-pass in the rendering-pipeline. | ||
- | By the " | ||
- | |||
- | <file pascal> | ||
- | FMyRenderPass := TMyRenderPass.Create(FGorilla); | ||
- | FMyRenderPass.Viewport := FGorilla; | ||
- | FMyRenderPass.Enabled := true; | ||
- | </ |