Unit CastleGLImages

DescriptionUsesClasses, Interfaces, Objects and RecordsFunctions and ProceduresTypesConstantsVariables

Description

Using images in OpenGL (as textures and as normal images).

For non-OpenGL image management, see CastleImages and TextureImages units. They contain functions and classes to load, save and process images.

This unit has functions and classes to:

  • Load images as OpenGL textures. You usually do not use these directly, instead TCastleScene automatically uses these to load and render textures as part of 3D models.

    A lot of utilities included: for 2D textures (see LoadGLTexture), cube maps (see glTextureCubeMap), 3D textures (see glTextureImage3D). These functions wrap OpenGL calls like glTexImage2D to handle our images (TEncodedImage (and descendant TCastleImage), TDDSImage), and to automatically set texture parameters, mipmaps and such.

  • Load and draw images in 2D. This is useful to implement various 2D controls. See TGLImage class and friends.

  • Save the current OpenGL screen contents to our TCastleImage. You usually use this through TCastleWindowBase.SaveScreen or TCastleControl.SaveScreen, based on SaveScreen_NoFlush in this unit.

  • Render to texture, see TGLRenderToTexture class. This is our abstraction over OpenGL framebuffer (or glCopyTexSubImage for ancient GPUs).

This unit hides from your some details about OpenGL images handling. For example, you don't have to worry about "pixel store alignment", we handle it here internally when transferring images between memory and GPU. You also don't have to worry about texture sizes being power of 2 (for OpenGLs with missing or buggy/slow ARB_texture_non_power_of_two), or about maximum texture sizes — we will resize textures if necessary.

Routines in this unit that take TCastleImage or TEncodedImage parameter are limited to TextureImageClassesAll (for routines dealing with textures) or PixelsImageClasses (for routines dealing with images drawn on 2D screen).

Uses

Overview

Classes, Interfaces, Objects and Records

Name Description
Class EImageClassNotSupportedForOpenGL  
Class TGLImage OpenGL image ready to be drawn on 2D screen.
Class ETextureLoadError  
Class ECannotLoadS3TCTexture  
Class EInvalidImageForOpenGLTexture  
Class TGLVideo Sequence of OpenGL textures to be played as a video.
Class EGenerateMipmapNotAvailable  
Class EFramebufferError  
Class EFramebufferSizeTooLow  
Class EFramebufferInvalid  
Class TGLRenderToTexture Rendering to texture with OpenGL.

Functions and Procedures

function ImageGLFormat(const Img: TCastleImage): TGLenum;
function ImageGLInternalFormat(const Img: TEncodedImage): TGLenum;
function ImageGLType(const Img: TCastleImage): TGLenum;
procedure ImageDraw(const Image: TCastleImage);
procedure ImageDrawRows(const Image: TCastleImage; Row0, RowsCount: integer);
procedure ImageDrawPart(const image: TCastleImage; const X0, Y0, Width, Height: Cardinal); overload;
procedure ImageDrawPart(const image: TCastleImage; const X0, Y0: Cardinal); overload;
function SaveScreen_NoFlush(xpos, ypos, width, height: integer; ReadBuffer: TGLenum): TRGBImage; overload;
function SaveScreen_NoFlush( ImageClass: TCastleImageClass; xpos, ypos, width, height: integer; ReadBuffer: TGLenum): TCastleImage; overload;
procedure SaveScreen_NoFlush( Image: TCastleImage; xpos, ypos: integer; ReadBuffer: TGLenum); overload;
function SaveAlignedScreen_NoFlush( const XPos, YPos: Integer; Width: Cardinal; const Height: Cardinal; const ReadBuffer: TGLenum): TRGBImage;
function SaveScreenToGL_NoFlush( const XPos, YPos: Integer; const Width, Height: Cardinal; const ReadBuffer: TGLenum): TGLImage;
procedure ResizeForTextureSize(var r: TCastleImage);
function ResizeToTextureSize(const r: TCastleImage): TCastleImage;
function IsTextureSized(const r: TEncodedImage): boolean;
function IsTextureSized(const Width, Height: Cardinal): boolean;
procedure ResizeToTextureSize(var Width, Height: Cardinal);
function IsCubeMapTextureSized(const Size: Cardinal): boolean;
function ResizeToCubeMapTextureSize(const Size: Cardinal): Cardinal;
operator = (const W1, W2: TTextureWrap2D): boolean;
operator = (const W1, W2: TTextureWrap3D): boolean;
function Texture2DClampToEdge: TTextureWrap2D;
function TextureMinFilterNeedsMipmaps(const MinFilter: TGLenum): boolean;
function LoadGLTexture(const image: TEncodedImage; MinFilter, MagFilter: TGLenum; const Wrap: TTextureWrap2D; GrayscaleIsAlpha: boolean = false; DDSForMipmaps: TDDSImage = nil): TGLuint; overload;
function LoadGLTexture(const FileName: string; MinFilter, MagFilter: TGLenum; const Wrap: TTextureWrap2D; GrayscaleIsAlpha: boolean = false; DDSForMipmaps: TDDSImage = nil): TGLuint; overload;
procedure LoadGLGeneratedTexture(texnum: TGLuint; const image: TEncodedImage; MinFilter, MagFilter: TGLenum; const Wrap: TTextureWrap2D; GrayscaleIsAlpha: boolean = false; DDSForMipmaps: TDDSImage = nil); overload;
procedure glTextureCubeMap( PositiveX, NegativeX, PositiveY, NegativeY, PositiveZ, NegativeZ: TEncodedImage; DDSForMipmaps: TDDSImage; Mipmaps: boolean);
procedure glTextureImage3D(const Image: TEncodedImage; MinFilter, MagFilter: TGLenum; DDSForMipmaps: TDDSImage);
function HasGenerateMipmap: boolean;
procedure GenerateMipmap(target: TGLenum);
procedure TexParameterMaxAnisotropy(const target: TGLenum; const Anisotropy: TGLfloat);
function GLDecompressS3TC(Image: TS3TCImage): TCastleImage;

Types

TTextureWrap2D = array [0..1] of TGLenum;
TTextureWrap3D = array [0..2] of TGLenum;
TGLRenderToTextureBuffer = (...);

Constants

PixelsImageClasses: array [0..3] of TCastleImageClass = ( TRGBImage, TRGBAlphaImage, TGrayscaleImage, TGrayscaleAlphaImage);
Texture2DRepeat: TTextureWrap2D = (GL_REPEAT, GL_REPEAT);

Description

Functions and Procedures

function ImageGLFormat(const Img: TCastleImage): TGLenum;

Return appropriate OpenGL format and type constants for given TCastleImage descendant. If you will pass here Img that is not a descendant of one of TextureImageClassesAll or PixelsImageClasses, they will raise EImageClassNotSupportedForOpenGL.

ImageGLInternalFormat works with TS3TCImage classes also, returning appropriate GL_COMPRESSED_*_S3TC_*_EXT, suitable for glCompressedTexImage2D.

Exceptions raised
EImageClassNotSupportedForOpenGL
When Img class is not supported by OpenGL.
function ImageGLInternalFormat(const Img: TEncodedImage): TGLenum;
 
function ImageGLType(const Img: TCastleImage): TGLenum;
 
procedure ImageDraw(const Image: TCastleImage);

Draw the image on 2D screen. Note that if you want to use this many times, it will be much faster to create TGLImage instance.

Exceptions raised
EImageClassNotSupportedForOpenGL
When Image class is not supported by OpenGL.
procedure ImageDrawRows(const Image: TCastleImage; Row0, RowsCount: integer);

Draw the subset of image rows on 2D screen. Draws RowsCount rows starting from Row0.

Exceptions raised
EImageClassNotSupportedForOpenGL
When Image class is not supported by OpenGL.
procedure ImageDrawPart(const image: TCastleImage; const X0, Y0, Width, Height: Cardinal); overload;

Draw a part of the image on 2D screen.

Part of the image starts from X0, Y0 (where 0, 0 is the left/bottom pixel, i.e. where the normal ImageDraw starts) and spans Width/Height. Overloaded version without Width, Height parameters just draws the whole remaining image.

Too large X0, Y0, Width, Height values are automatically detected and cut as appropriate, so you can safely pass any large values here.

This will cut of some columns at the left/right and bottom/top by using tricks with OpenGL pixel store unpack (don't worry, the whole state of pixel store unpack will be taken care of and preserved by this). So it works fast.

Exceptions raised
EImageClassNotSupportedForOpenGL
When Image class is not supported by OpenGL.
procedure ImageDrawPart(const image: TCastleImage; const X0, Y0: Cardinal); overload;
 
function SaveScreen_NoFlush(xpos, ypos, width, height: integer; ReadBuffer: TGLenum): TRGBImage; overload;

Save the current color buffer contents to image. Does glReadBuffer(ReadBuffer) and then glReadPixels with appropriate parameters.

The suffix "noflush" in the name is there to remind you that this function grabs the current buffer contents. Usually you want to call something like TCastleWindowBase.FlushRedisplay right before grabbing from the front buffer (which isn't reliable anyway), or redraw (like by TCastleWindowBase.EventDraw) right before grabbing from the back buffer.

See TCastleWindowBase.SaveScreen for more friendly ways to capture the screen.

Version with ImageClass can save to any image format from PixelsImageClasses.

Version with TCastleImage instance just uses this instance to save the image. You must pass here already created TCastleImage instance, it's class, Width and Height will be used when saving.

We always take explicit Width, Height (from parameter, or from Image.Width, Image.Height). Guessing screen size automatically doesn't really work, as the viewport may change when we use custom viewports.

Exceptions raised
EImageClassNotSupportedForOpenGL
When Image class is not supported by OpenGL.
function SaveScreen_NoFlush( ImageClass: TCastleImageClass; xpos, ypos, width, height: integer; ReadBuffer: TGLenum): TCastleImage; overload;
 
procedure SaveScreen_NoFlush( Image: TCastleImage; xpos, ypos: integer; ReadBuffer: TGLenum); overload;
 
function SaveAlignedScreen_NoFlush( const XPos, YPos: Integer; Width: Cardinal; const Height: Cardinal; const ReadBuffer: TGLenum): TRGBImage;

Save the screen, except it may make the width larger, to make it divisible by four, to workaround Radeon bug TGLVersion.BuggyDrawOddWidth.

If GLVersion.BuggyDrawOddWidth then it will eventually enlarge the Width to make it a multiple of 4. Possibly, multiple of 2 would be enough, but you don't want to risk with Radeon bugs...

You can draw this image by normal ImageDraw, although you risk then that you will see an additional column at the right filled with garbage colors (due to enlarging of screen done here). Ideally, it would be best to draw this only by ImageDrawPart(0, 0, Width (given here, not Image.Width), Image.Height) but it may not be possible — again, thanks to TGLVersion.BuggyDrawOddWidth.

function SaveScreenToGL_NoFlush( const XPos, YPos: Integer; const Width, Height: Cardinal; const ReadBuffer: TGLenum): TGLImage;

Captures current screen as a TGLImage instance, ready to be drawn on 2D screen.

procedure ResizeForTextureSize(var r: TCastleImage);

Resize the image to a size accepted as GL_TEXTURE_2D texture size for OpenGL. It tries to resize to a larger size, not smaller, to avoid losing image information. Usually you don't have to call this, LoadGLTexture* functions call it automatically when needed.

function ResizeToTextureSize(const r: TCastleImage): TCastleImage;
 
function IsTextureSized(const r: TEncodedImage): boolean;

Does image have proper size for OpenGL texture (GL_TEXTURE_2D). That is, for passing to glTexImage2D for GL_TEXTURE_2D target. This checks glGet(GL_MAX_TEXTURE_SIZE), so requires initialized OpenGL context.

function IsTextureSized(const Width, Height: Cardinal): boolean;
 
procedure ResizeToTextureSize(var Width, Height: Cardinal);
 
function IsCubeMapTextureSized(const Size: Cardinal): boolean;
 
function ResizeToCubeMapTextureSize(const Size: Cardinal): Cardinal;
 
operator = (const W1, W2: TTextureWrap2D): boolean;
 
operator = (const W1, W2: TTextureWrap3D): boolean;
 
function Texture2DClampToEdge: TTextureWrap2D;

Return wrap GL_CLAMP_TO_EDGE in both directions.

function TextureMinFilterNeedsMipmaps(const MinFilter: TGLenum): boolean;
 
function LoadGLTexture(const image: TEncodedImage; MinFilter, MagFilter: TGLenum; const Wrap: TTextureWrap2D; GrayscaleIsAlpha: boolean = false; DDSForMipmaps: TDDSImage = nil): TGLuint; overload;

Load new texture to OpenGL. Generates new texture number by glGenTextures, then binds this texture, and loads it's data.

Takes care of UNPACK_ALIGNMENT inside (if needed, we'll change it and later revert back, so that the texture is correctly loaded).

Sets texture minification, magnification filters and wrap parameters.

Changes currently bound texture to this one (returned).

GrayscaleIsAlpha is meaningful only if the image is TGrayscaleImage class. If GrayscaleIsAlpha is False, then we'll load GL_LUMINANCE texture (this basically behaves like normal RGB texture, except that it has only one channel and stores grayscale colors). If GrayscaleIsAlpha is True, the texture will be loaded as GL_ALPHA texture (it will modify only the fragments alpha value, it doesn't have any "color" in the normal sense, it's only for opacity).

If mipmaps will be needed (this is decided looking at MinFilter) we will load them too.

  1. As a first try, if DDSForMipmaps is non-nil and has mipmaps (DDSForMipmaps.Mipmaps), we will load these mipmaps. DDSForMipmaps must be a normal 2D texture (DDSType = dtTexture).

    Otherwise, we'll try to generate mipmaps, using various OpenGL mechanisms.

  2. If GenerateMipmap functionality will be required to create mipmaps, but is not available on this OpenGL implementation, we will change MinFilter to simple GL_LINEAR and make OnWarning. So usually you just don't have to worry about this. Note that current implementation requires GenerateMipmap functionality only for S3TC textures, for normal uncompressed textures we can generate mipmaps on CPU or through SGIS_GENERATE_MIPMAP extension.

Exceptions raised
ETextureLoadError
If texture cannot be loaded for whatever reason. This includes ECannotLoadS3TCTexture if the S3TC texture cannot be loaded for whatever reason. This includes EInvalidImageForOpenGLTexture if Image class is invalid for an OpenGL texture.
EImageClassNotSupportedForOpenGL
When Image class is not supported by OpenGL.
function LoadGLTexture(const FileName: string; MinFilter, MagFilter: TGLenum; const Wrap: TTextureWrap2D; GrayscaleIsAlpha: boolean = false; DDSForMipmaps: TDDSImage = nil): TGLuint; overload;
 
procedure LoadGLGeneratedTexture(texnum: TGLuint; const image: TEncodedImage; MinFilter, MagFilter: TGLenum; const Wrap: TTextureWrap2D; GrayscaleIsAlpha: boolean = false; DDSForMipmaps: TDDSImage = nil); overload;

Load OpenGL texture into already reserved texture number. It uses existing OpenGL texture number (texnum). Everything else works exactly the same as LoadGLTexture.

You can also use this to set "default unnamed OpenGL texture" parameters by passing TexNum = 0.

Exceptions raised
ETextureLoadError
Raised in the same situations as LoadGLTexture.
EImageClassNotSupportedForOpenGL
When Image class is not supported by OpenGL.
procedure glTextureCubeMap( PositiveX, NegativeX, PositiveY, NegativeY, PositiveZ, NegativeZ: TEncodedImage; DDSForMipmaps: TDDSImage; Mipmaps: boolean);

Comfortably load all six cube map texture images. Think about this as doing glTexImage2D(Side, ...) for each cube side. It takes care of (almost?) everything you need to prepare OpenGL cube map texture.

It automatically takes care to adjust the texture size to appropriate size, honoring the "power of two" requirement and the GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB limit of OpenGL. So texture image may be resized (preferably up) internally before loading. Although, if texture is S3TC compressed, we cannot resize it — so ECannotLoadS3TCTexture will be raised if texture is not appropriate size.

It takes care about OpenGL unpack parameters. Just don't worry about it.

If mipmaps are requested:

  1. First of all, if DDSForMipmaps is non-nil and has mipmaps defined, we will load them from this DDS image. DDSForMipmaps must have DDSType = dtCubeMap.

  2. Otherwise, we'll try to generate images using OpenGL GenerateMipmap.

  3. As a last resort, if GenerateMipmap is not available, we will fallback to generating mipmaps on CPU by good old gluBuild2DMipmaps call.

Exceptions raised
ETextureLoadError
If texture cannot be loaded for whatever reason. This includes ECannotLoadS3TCTexture if the S3TC texture cannot be loaded for whatever reason (not availble S3TC extensions, not correct texture size, mipmaps requested and DDSForMipmaps/glGenerateMipmap not available). This includes EInvalidImageForOpenGLTexture if Image class is invalid for an OpenGL texture.
EImageClassNotSupportedForOpenGL
When Image class is not supported by OpenGL.
procedure glTextureImage3D(const Image: TEncodedImage; MinFilter, MagFilter: TGLenum; DDSForMipmaps: TDDSImage);

Comfortably load a 3D texture. Think about this as doing glTexImage3D(...) for you. It also sets texture minification, magnification filters and creates mipmaps if necessary.

It checks OpenGL 3D texture size requirements, and throws exceptions if not satisfied.

It takes care about OpenGL unpack parameters. Just don't worry about it.

If MinFilter uses mipmaps, then all mipmap levels will be loaded.

  1. As a first try, if DDSForMipmaps is non-nil and has mipmaps (DDSForMipmaps.Mipmaps), we will load these mipmaps. DDSForMipmaps must be a 3D texture (DDSType = dtVolume).

  2. Otherwise, we'll generate mipmaps.

    GenerateMipmap functionality will be required for this. When it is not available on this OpenGL implementation, we will change MinFilter to simple GL_LINEAR and make OnWarning. So usually you just don't have to worry about this.

Exceptions raised
ETextureLoadError
If texture cannot be loaded for whatever reason, for example it's size is not correct for OpenGL 3D texture (we cannot automatically resize 3D textures, at least for now). Or it's compressed (although we support here TEncodedImage, OpenGL doesn't have any 3D texture compression available.)
EImageClassNotSupportedForOpenGL
When Image class is not supported by OpenGL.
function HasGenerateMipmap: boolean;

Is GenerateMipmap avaiable. This checks some GL extensions/versions that give us glGenerateMipmap or glGenerateMipmapEXT call, used by GenerateMipmap.

procedure GenerateMipmap(target: TGLenum);

Call glGenerateMipmap (or analogous function from some OpenGL extension).

Exceptions raised
EGenerateMipmapNotAvailable
If no glGenerateMipmap version is available on this OpenGL version. If you don't want to get this exception, you can always check HasGenerateMipmap before calling this.
procedure TexParameterMaxAnisotropy(const target: TGLenum; const Anisotropy: TGLfloat);

Call glTexParameterf to set GL_TEXTURE_MAX_ANISOTROPY_EXT on given texture target.

Takes care to check for appropriate OpenGL extension (if not present, does nothing), and to query OpenGL limit for Anisotropy (eventually clamping provided Anisotropy down).

function GLDecompressS3TC(Image: TS3TCImage): TCastleImage;

Decompress S3TC image by loading it to temporary OpenGL texture and reading back. So this internally uses current OpenGL context.

Exceptions raised
ECannotLoadS3TCTexture
If cannot decompress S3TC, for example because we cannot load to OpenGL this S3TC texture (because OpenGL S3TC extensions are not available, or such).

Types

TTextureWrap2D = array [0..1] of TGLenum;
 
TTextureWrap3D = array [0..2] of TGLenum;
 
TGLRenderToTextureBuffer = (...);
 
Values
  • tbColor:  
  • tbDepth:  
  • tbColorAndDepth:  
  • tbNone:  

Constants

PixelsImageClasses: array [0..3] of TCastleImageClass = ( TRGBImage, TRGBAlphaImage, TGrayscaleImage, TGrayscaleAlphaImage);
 
Texture2DRepeat: TTextureWrap2D = (GL_REPEAT, GL_REPEAT);
 

Generated by PasDoc 0.12.1 on 2013-02-04 20:26:50