PDA

View Full Version : How to use GLSL with JEDI-SDL?



cragwolf
15-04-2006, 01:12 AM
Does anyone know of an FPC or Delphi demo that shows how to use the OpenGL shading language with JEDI-SDL? Do I need the latest OpenGL headers? Are the ones that come with FPC or JEDI-SDL good enough?

vgo
15-04-2006, 07:04 AM
The headers that come with FPC/Lazarus are for OpenGL 1.1, so they are definitely too old.
The headers from JEDI-SDL seem to be outdated too, I'm using the dglOpenGL (http://www.delphigl.com) headers, they should compile on Linux too, but I haven't tried it yet. At least I can compile my test project using the headers with FPC and it runs.

technomage
15-04-2006, 09:25 AM
The headers in the JEDI-SDL cvs are up to date and support OpenGL 2.0. These are being tested (by me). I have got a basic framework for GLSL I'll try and post it but you will need the headers from cvs for it to work.

cragwolf
15-04-2006, 11:31 AM
Thanks, I'd much appreciate that framework.

grudzio
18-04-2006, 11:53 AM
There is a tutorial at http://wiki.delphigl.com/index.php/Tutorial#Shader, but it is in german.

cragwolf
18-04-2006, 12:49 PM
Danke sch??n! 8)

vgo
23-04-2006, 09:41 AM
I'm having trouble with the shaders... The shader loads just fine and I can set the uniform parameters, but I get "invalid operation" on glEnd();

Here's the vertex program:


void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}


Fragment program:


uniform sampler2D colorMap;

void main(void)
{
gl_FragColor = texture2D( colorMap, gl_TexCoord[0].st);
}


Here's the test code:

glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Textures[0]);
glCheckForError();
Shader.Enable;
glCheckForError();
Shader.SetParameter('colorMap', GL_TEXTURE0_ARB);
glCheckForError();

glBegin(GL_QUADS);
glColor3f(1, 1, 1);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
glVertex3f(10, -10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
glVertex3f(10, 10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
glVertex3f(-10, 10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
glVertex3f(-10, -10, -30);
glEnd();
glCheckForError(); // Error here

Shader.Disable;


The shader class is just a simple wrapper for loading the programs and setting parameters that I made for testing.


unit uShader;
{
Project Miniverse
http://www.projectminiverse.com

Description:
GLSL shader object

Classes:
TShader

Created:
22.04.2006 (JA)

Modified:

}

interface

uses
{$IFDEF USE_SDL}
GL, GLU, GLEXT,
{$ELSE}
dglOpenGL,
{$ENDIF}
glShader, glGeometry;

type
TShader = class(TObject)
protected
FProgramObject: GLHandleARB;
FName: String;
FResult: String;
public
constructor Create(FileName: String); overload;
constructor Create(const Name, VertexProgram, FragmentProgram: String); overload;
destructor Destroy; override;

procedure Enable;
procedure Disable;
procedure SetParameter(const Name: String; Value: TInt); overload;
procedure SetParameter(const Name: String; Value: TFloat); overload;
procedure SetParameter(const Name: String; Value1, Value2, Value3, Value4: TFloat); overload;

property Name: String read FName;
property ProgramObject: GLHandleARB read FProgramObject;
property Result: String read FResult;
end;

implementation

uses
glUtils;

constructor TShader.Create(FileName: String);
begin
inherited Create;
end;

constructor TShader.Create(const Name, VertexProgram, FragmentProgram: String);
begin
inherited Create;
FName := Name;
FProgramObject := glShader.LoadFragmentandVertexShader(FragmentProgr am, VertexProgram);
FResult := glShader.CheckForErrors(FProgramObject);
glCheckForError();
end;

destructor TShader.Destroy;
begin
inherited Destroy;
end;

procedure TShader.Enable;
begin
glUseProgramObjectARB(FProgramObject);
end;

procedure TShader.Disable;
begin
glUseProgramObjectARB(0);
end;

procedure TShader.SetParameter(const Name: String; Value: TInt);
var
p: PGLcharARB;
l: TInt;
begin
p := PGLCharArb(Name);
glUniform1iARB(glGetUniformLocationARB(FProgramObj ect, p), Value);
end;

procedure TShader.SetParameter(const Name: String; Value: TFloat);
var
p: PGLcharARB;
l: TInt;
begin
p := PGLCharARB(Name);
glUniform1fARB(glGetUniformLocationARB(FProgramObj ect, p), Value);
end;

procedure TShader.SetParameter(const Name: String; Value1, Value2, Value3, Value4: TFloat);
var
p: PGLcharARB;
l: TInt;
begin
p := PGLCharARB(Name);
glUniform4fARB(glGetUniformLocationARB(FProgramObj ect, p), Value1, Value2, Value3, Value4);
end;

end.


I tried to search for possible reason, but I can't seem to find any.

technomage
23-04-2006, 01:32 PM
Doesn't look like a problem with the Shader Programs. I tested the code in the ShaderDesigner and it works. I would first try to get the copde working without a shader (just use the fixed functionality) then add the shader once that is working. There might be a problem in the setup/initialization of the app which is causing the error.

vgo
23-04-2006, 02:23 PM
It works if I use ARB multitexturing, but as soon as I try to use the shader I get the error.

I load the ARB extensions for multitexturing and shaders, ie. GL_ARB_multitexture and GL_ARB_shader_objects, do I need something else too? Do I need to initialize the OpenGL pipeline for shaders before using them? Doesn't seem likely as I can load and compile the shaders and the driver says: "Link successful. The GLSL vertex shader will run in hardware. The GLSL fragment shader will run in hardware."

Here's my OpenGL initialization code:

glClearColor(0.0, 0.0, 0.0, 0.0);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);


Didn't find any documentation to explain what can I do and what I cannot do when using shaders...

technomage
23-04-2006, 03:23 PM
I'll have a look at something similar here and see if I can get it working.

technomage
23-04-2006, 03:51 PM
Works fine here.

Try changing

Shader.SetParameter('colorMap', GL_TEXTURE0_ARB);

to

Shader.SetParameter('colorMap', Textures[0]);

That might work, as I think it's the texture ID you need to pass in there not the opengl constant.

vgo
23-04-2006, 04:13 PM
Umm... No?

Aren't you supposed to pass the id of the texture unit, that has the texture binded, to use as sampler2D object in the shader, not the texture itself?

I tried that too and it doesn't work any better...

Any chance that you could post some kind of simple example using the shaders ie. a complete program? So that I could compare it to my code and see what's the difference and where it goes wrong...

vgo
23-04-2006, 06:00 PM
I'll try to make as simple program as possible using shaders and see if it still produces the error.

EDIT: Here's the program.


The shader class:

unit uShader;
{
Project Miniverse
http://www.projectminiverse.com

Description:
GLSL shader object

Classes:
TShader

Created:
22.04.2006 (JA)

Modified:

}

interface

uses
{$IFDEF USE_SDL}
GL, GLU, GLEXT,
{$ELSE}
dglOpenGL,
{$ENDIF}
glGeometry;

type
TShader = class(TObject)
protected
FProgramObject: GLHandleARB;
FName: String;
FResult: String;
public
constructor Create(FileName: String); overload;
constructor Create(const Name, VertexProgram, FragmentProgram: String); overload;
destructor Destroy; override;
function CheckForErrors(glObject: GLHandleARB): String;
function LoadFragmentandVertexShader(FFilename: string; VFilename: string): GLHandleARB;

procedure Enable;
procedure Disable;
procedure SetParameter(const Name: String; Value: TInt); overload;
procedure SetParameter(const Name: String; Value: TFloat); overload;
procedure SetParameter(const Name: String; Value1, Value2, Value3, Value4: TFloat); overload;

property Name: String read FName;
property ProgramObject: GLHandleARB read FProgramObject;
property Result: String read FResult;
end;

implementation

uses
Classes,
SysUtils,
glUtils;

constructor TShader.Create(FileName: String);
begin
inherited Create;
end;

constructor TShader.Create(const Name, VertexProgram, FragmentProgram: String);
begin
inherited Create;
FName := Name;
FProgramObject := LoadFragmentandVertexShader(FragmentProgram, VertexProgram);
FResult := CheckForErrors(FProgramObject);
glCheckForError();
end;

destructor TShader.Destroy;
begin
inherited Destroy;
end;

function TShader.CheckForErrors(glObject: GLHandleARB): String;
var
blen, slen: GLInt;
InfoLog : PGLCharARB;
begin
glGetObjectParameterivARB(glObject, GL_OBJECT_INFO_LOG_LENGTH_ARB, @blen);
if blen > 1 then
begin
GetMem(InfoLog, blen*SizeOf(GLCharARB));
{$IFDEF USE_SDL}
glGetInfoLogARB(glObject, blen , @slen, InfoLog);
{$ELSE}
glGetInfoLogARB(glObject, blen , slen, InfoLog);
{$ENDIF}
Result:= PChar(InfoLog);
Dispose(InfoLog);
end;
end;

function TShader.LoadFragmentandVertexShader(FFilename: string; VFilename: string): GLHandleARB;
var
ProgramObject, FragmentShaderObject, VertexShaderObject: GLHandleARB;
FShader: TStringList;
FShaderText: String;
FShaderLength: Integer;
VShader: TStringList;
VShaderText: String;
VShaderLength: Integer;
begin
Result := 0;
if FileExists(FFilename) then
if FileExists(VFilename) then
begin
ProgramObject := glCreateProgramObjectARB;

FragmentShaderObject := glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
VertexShaderObject := glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);

FShader := TStringList.Create;
FShader.LoadFromFile(FFilename);
FShaderText := FShader.Text;
FShaderLength := Length(FShaderText);

VShader := TStringList.Create;
VShader.LoadFromFile(VFilename);
VShaderText := VShader.Text;
VShaderLength := Length(VShaderText);

glShaderSourceARB(VertexShaderObject, 1, @VShaderText, @VShaderLength);
glShaderSourceARB(FragmentShaderObject, 1, @FShaderText, @FShaderLength);

glCompileShaderARB(FragmentShaderObject);
glCompileShaderARB(VertexShaderObject);

glAttachObjectARB(ProgramObject, FragmentShaderObject);
glAttachObjectARB(ProgramObject, VertexShaderObject);

glDeleteObjectARB(FragmentShaderObject);
glDeleteObjectARB(VertexShaderObject);
glLinkProgramARB(ProgramObject);

Result := ProgramObject;
end;
end;

procedure TShader.Enable;
begin
glUseProgramObjectARB(FProgramObject);
end;

procedure TShader.Disable;
begin
glUseProgramObjectARB(0);
end;

procedure TShader.SetParameter(const Name: String; Value: TInt);
var
p: PGLcharARB;
begin
p := PGLCharArb(Name);
glUniform1iARB(glGetUniformLocationARB(FProgramObj ect, p), Value);
end;

procedure TShader.SetParameter(const Name: String; Value: TFloat);
var
p: PGLcharARB;
begin
p := PGLCharARB(Name);
glUniform1fARB(glGetUniformLocationARB(FProgramObj ect, p), Value);
end;

procedure TShader.SetParameter(const Name: String; Value1, Value2, Value3, Value4: TFloat);
var
p: PGLcharARB;
begin
p := PGLCharARB(Name);
glUniform4fARB(glGetUniformLocationARB(FProgramObj ect, p), Value1, Value2, Value3, Value4);
end;

end.


And the test program:

program SDL_OpenGLTest;

uses
SysUtils,
GL, GLU, GLEXT,
SDL,
Windows,
uShader,
uTextureManager;

const
// screen width, height, and bit depth
SCREEN_WIDTH = 640;
SCREEN_HEIGHT = 480;
SCREEN_BPP = 32;

MAX_EXTENSION = 4;
Extensions: array [0..MAX_EXTENSION] of String = (
'GL_ARB_multitexture',
'GL_ARB_texture_cube_map',
'GL_ARB_fragment_shader',
'GL_ARB_vertex_shader',
'GL_ARB_shader_objects'
);

type
EOpenGLError = class(Exception);

var
// This is our SDL surface
surface : PSDL_Surface;
Ready: Boolean;
Shader: TShader;
Textures: array [0..1] of GLuInt;

procedure glCheckForError;
var
glError: GLenum;
begin
glError := glGetError();
if (glError <> GL_NO_ERROR) then
raise EOpenGLError.Create('OpenGL error! Code: ' + IntToStr(glError) + ' ($' + IntToHex(glError, 4) + ' hex) Description: ' + gluErrorString(glError));
end;

procedure TerminateApplication;
begin
SDL_QUIT;
Halt(0);
end;

procedure HandleError(ErrClass: String; ErrException: Exception; ErrMethod: String);
begin
Ready := FALSE;
MessageBox(0, PChar('Class: ' + ErrClass + #13#10 + 'Method: ' + ErrMethod + #13#10 + 'Exception: ' + ErrException.ClassName + #13#10 + 'Message: ' + ErrException.Message), 'Error!', MB_ICONERROR or MB_OK);
TerminateApplication;
end;

// function to reset our viewport after a window resize

function ResizeWindow( width : integer; height : integer ) : Boolean;
var
h: Integer;
begin
try
// Protect against a divide by zero
if (Height = 0) then
h := 1
else
h := Height;

// Setup our viewport.
glViewport( 0, 0, Width, h );

// Change to the projection matrix and set our viewing volume.
glMatrixMode( GL_PROJECTION );
glLoadIdentity;

// Set our perspective
gluPerspective( 45.0, Width / h, 0.1, 1000.0 );

// Make sure we're changing the model view and not the projection
glMatrixMode( GL_MODELVIEW );

// Reset The View
glLoadIdentity();

glEnable(GL_TEXTURE_2D);
Result := TRUE;
except
raise;
end;
end;

// function to handle key press events

procedure HandleKeyPress( keysym : PSDL_keysym );
begin
case keysym^.sym of
SDLK_ESCAPE :
// ESC key was pressed
TerminateApplication;
end;
end;

// A general OpenGL initialization function. Sets all of the initial parameters.

procedure InitGL;
// We call this right after our OpenGL window is created.
var
s: String;
i: Integer;
begin
gTextureManager := TTextureManager.Create;
if not glext_LoadExtension('GL_version_1_2') then
s := 'OpenGL version 1.2 not supported!' + #13#10;
// Load extensions
for i := 0 to MAX_EXTENSION do
if glext_ExtensionSupported(PChar(Extensions[i]), #0) then
glext_LoadExtension(Extensions[i])
else
s := Extensions[i] + #13#10;
if s <> '' then
raise EOpenGLError('All needed OpenGL extensions are not supported! Unsupported extensions: ' + #13#10 + s);
glClearColor(0.0, 0.0, 0.0, 0.0);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
glCheckForError();
Textures[0] := gTextureManager.GetTexture('Data\Textures\Misc\Che cker.bmp');
Textures[1] := gTextureManager.GetTexture('Data\Textures\Misc\Ope nGL.bmp');
Shader := TShader.Create('SimpleShader', 'Shaders\texture.vp', 'Shaders\texture.fp');
glCheckForError();
ResizeWindow(SCREEN_WIDTH, SCREEN_HEIGHT);
Ready := TRUE;
end;

// The main drawing function.

procedure DrawGLScene;
begin
if not Ready then
Exit;

try
glClear( GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT );
glLoadIdentity();
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Textures[0]);
//glActiveTextureARB(GL_TEXTURE1_ARB);
//glEnable(GL_TEXTURE_2D);
//glBindTexture(GL_TEXTURE_2D, Textures[1]);
//glCheckForError();

Shader.Enable;
glCheckForError();
Shader.SetParameter('colorMap', GL_TEXTURE0_ARB);
glCheckForError();

glColor3f(1, 1, 1);
glBegin(GL_QUADS);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
//glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
glVertex3f(10, -10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
//glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
glVertex3f(10, 10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
//glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
glVertex3f(-10, 10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
//glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
glVertex3f(-10, -10, -30);
glEnd();

glCheckForError();
Shader.Disable;

// swap buffers to display, since we're double buffered.
SDL_GL_SwapBuffers;
except
on E: Exception do
HandleError('Application', E, 'DrawGLScene');

end;
end;

var
Done : Boolean;
event : TSDL_Event;
videoflags : Uint32;
videoInfo : PSDL_VideoInfo;
begin
// Initialize SDL
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) then
begin
TerminateApplication;
end;

// Fetch the video info
videoInfo := SDL_GetVideoInfo;

if ( videoInfo = nil ) then
begin
TerminateApplication;
end;

// the flags to pass to SDL_SetVideoMode
videoFlags := SDL_OPENGL; // Enable OpenGL in SDL
videoFlags := videoFlags or SDL_DOUBLEBUF; // Enable double buffering
videoFlags := videoFlags or SDL_HWPALETTE; // Store the palette in hardware

// This checks to see if surfaces can be stored in memory
if ( videoInfo^.hw_available <> 0 ) then
videoFlags := videoFlags or SDL_HWSURFACE
else
videoFlags := videoFlags or SDL_SWSURFACE;

// This checks if hardware blits can be done * /
if ( videoInfo^.blit_hw <> 0 ) then
videoFlags := videoFlags or SDL_HWACCEL;

// Set the OpenGL Attributes
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

// Set the title bar in environments that support it
SDL_WM_SetCaption( 'SDL OpenGL test', nil );

videoflags := videoFlags or SDL_RESIZABLE; // Enable window resizing
//videoflags := videoFlags or SDL_FULLSCREEN; // Enable window resizing

surface := SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, videoflags );
if ( surface = nil ) then
begin
TerminateApplication;
end;

InitGL;

// Loop, drawing and checking events
Done := FALSE;
while ( not Done ) do
begin
// This could go in a separate function
while ( SDL_PollEvent( @event ) = 1 ) do
begin
case event.type_ of
SDL_QUITEV :
begin
Done := true;
end;

SDL_KEYDOWN :
begin
// handle key presses
HandleKeyPress( @event.key.keysym );

end;

SDL_VIDEORESIZE :
begin
surface := SDL_SetVideoMode( event.resize.w, event.resize.h, SCREEN_BPP, videoflags );
if ( surface = nil ) then
begin
TerminateApplication;
end;
ResizeWindow( event.resize.w, event.resize.h );
end;
end;
end;
// draw the scene
DrawGLScene;
end;
TerminateApplication;
end.


The TextureManager class loads/saves/caches the textures, you can replace it with something similar (it would be too big to post here).

Any ideas why it doesn't work?

grudzio
23-04-2006, 09:39 PM
The texture units are indexed from 0 to n -1 where n is the number of avaiable texture units.
So to use first unit send 0 instad of GL_TEXTURE0_ARB and everything should be okay.

vgo
24-04-2006, 01:26 PM
Yes! That's it! Now it works!

Thanks a lot. :)