-, -V
/--
\"
-'
. . ,\342\200\242
\302\273,<
\342\200\242
'-'-,''
-
,.
developing
In has
a fixed
the
pipeline,
graphics
functionality.
\\
' -, \342\200\242
- .
,
l
we assumed that each
when we wanted
.
.
- .: -\34
. :\342\200\242'fi>.
l . -\342\200\242--1\342\200\242'\\\\\342\200\242\342\
.
l
Consequently,
colors
of an
'
!- .> .i v i - ,;i
\342\200\242-
in the pipeline
light-material
to the modified
the fixed-function
box
to
use
interactions to determine the
object, we were limited
Phong model becauseit was the only lighting model supported
by
pipeline in the OpenGL specification and, until recently, the only model supported
to having
model
available, lighting
by mo st hardware. In addition
only one hghting
calculations were doneonlyfor
each vertex.
The resulting vertex colorswere then inr agmen t processor. If w e wanted
over the primitive
terpolated
by thefixed-functionf
to use some other lighting
or shading model, we had to resort
to an off-line renderer.
Over the past few years, graphics processors have changed dramatically.
Both
the vertex processor and fragment
are
now
user
We
can
processor
programmable.
called vertex shaders and fragment
shaders to achieve complexvisual
writeprograms
i p elin e .
effects
at the same rate as the standardfixed-functionp
In this
we introduce the concept of programmable
shaders.
First, we
chapter,
review some of the efforts to develop languages to describe shaders.These
efforts
in the OpenGLShadingLanguage (GLSL),
culminated
which
is now a standard part
of OpenGL.
We t he n use GLSL t o develop a variety
of vertex shaders that compute
vertex properties, including
their
and colors. Finally,
we develop
positions
fragment
shaders that let us program the calculations
on each fragment and ultiperformed
Our discussion of fragment
shaders
will
mately determine the color of each
pixel.
alsointroduce many new ways of using texture mapping.
9.1
PROGRAMMABLE PIPELINES
we developed
the Phong and modified
6,
Phong hghting models in Chapter
we placed great emphasis
on their efficiency
but much less on how
well they modeled
model is remarkable in
physical light-material interactions. The modified
Phong
that
while it has a very
loose
with
it yields images that
coupling
physical reality,
When
are adequatefor
architectures
of OpenGL
most
developed,
and was
purposes.
it was
implemented
Hence, as graphics
natural
that
this
in commodity
and especially pipeline
was built into the specification
hardware
model
hardware.
451
452
Ch a p t e r9Pr
ogr
Shaders
a m m a ble
Normalized
Object
coordinates
Vertices
\342\200\224\342\200\242?
C l ip
coord i n a te s
JC
d evice
coor d in a tes
lippin g and
^fta^rizer
|_
^F ragm
ent
^Pixe|
s
assemph
primitive
FIGURE 9.1
j
Window
coor a in a re s
Pipeline architecture.
The modified
model is adequate for simulating
smooth
surfaces, such
Phong
as plastic or metal, which have an isotropic
BDRF. That is, the properties
of the
material are the same in all directions. Often, however,
we need a more physically
realistic
model th at can model materials
with
BDRFs that
asymmetric
(anisotropic)
and fluids. When
we
skin, fabrics,
many real world materials, including
with translucent materials, we wan t to incorporate refraction, the bending
of
light as it passes through materials with different properties. We mi ght also want to
account for the fact that howlight isbentbyrefraction is a function
ofth e wavelength
characterize
work
of the
light.
effects. For example, we might
situations, we want nonphotorealistic
the brush strokes ofa painter or we might want to create cartoonlike
effects.
can be achieved onlybyworking with
shading
Many ofth ese effects
fragments
in ways that are not possible-with
i p elin e . For example, in bump
afixed-functionp
a topic that we consider in Section
for each
9.12, we change the normal
mapping,
to
the
of
a
surface
with
that
fragment
give
appearance
great
complexity
appears
correct aseither the lights or the surface move. Many of these algorithms
and effects
were developed more than 20 years ago but were only available
non-realthrough
time Tenderers, such as RenderMan.
Recent advances in graphics
architectures
have
altered this situation
dramatically.
in Figure 9.1. This figure
Consider the pipeline architecture
illustrated
represents the same architecture
we have been discussing since Chapter 1. First,
we
vertices.
Note
that
because
both
the
model-view
and
transforprocess
projection
in eye
mations are applied
vertex processing, the representation
of vertices
during
coordinates occurs only within the vertex processer. At the end of vertex processeach vertex has had its loca tion transformed
ing,
by the model-viewand projection
has
been
a
and
on which options are
matrices,
usually
assigned color,
depending
such as texture coordinates have
been
Vertices
enabled,other attributes
assigned.
are then assembled
into primitives that are clipped.The potentially
visible primitives that are not clipped out are rasterized, generating
the
fragments.
Ultimately,
the final display. What
has changed
is that the
fragments are processedto generate
r o cessor are now programmable
vertex processorand thefragmentp
by applicationcalled
shaders
that
are compiled
and loaded into the graphics
specific programs
In other
want
to simulate
processor.
'
9.2 Shading
9.2
LANGUAGES
SHADING
we can develop a programming
model
for shaders, we need a method
to deAn approach that was inspired
scribe lighting models and shaders.
by the RenderMan
shading lang uag e is to look at shaders as mathematical expressions in a language that
Before
involves variables, constants,
and operations
among these entities. Such expressions
can also be expressedin the form of tree data structures,
and these trees can be traversedby a variety of algorithms to evaluate
the expression
represented by the tree.
between
th at describe
and
Hence,there is a dir ect relationship
languages
expressions
these
algorithms to evaluate
expressions.
Shade Trees
9.2 .1
the
Consider
original
Phong shading
model from
6, without
Chapter
the distance
term:
I = kdLdl-n
a
When
+KLaIa-
+ksLs(*-y)a
evaluates
system
graphics
OpenGLstate,
and r is computedby
r=2(1\342\200\242n)n
-1.
of these
Both
ducting
equations
exponentiation,
resent theseequations
type
are known
nal
nodes
and all
the
child
1
n
1 are
and
known
from
the
inthat involve bot h arithmetic
expressions
operations,
vector operations, such as the dot product.
We can rep2
a tree data structure
as shown in Figure 9.2. Treesof
using
trees.
Variables
and
constants
expression
appear at the termiare
and
as
internal nodes represent operations.Because
tree is a bina ry tree in which all internal
resulting
this
binary,
this expression,
operators are
have exacdy two
all our
nodes
nodes.
to traversing
the correspondEvaluating an arithmetic expression is equivalent
that
node
and
out
the
tree,
is, visiting every
ing
carrying
requisite mathematical
In
at
the
internal
nodes.
this
trees
and
their
associated
traversal
sense,
operations
and implementing
mathematical
algorithms provide a means of both representing
expressions,such as that for the Phong shader.
The more interesting
of expression trees is in designing
new shaders.
application
in a graphics system and
Given a setof variables
that might be available
operations
that are supported by the system, we can form collectionsof trees;
each collection
of one or more treesdefines
a different
method for shading
a surface. This approach
is taken
in the
RenderMan
shading
language.
Variables,
such
as normals
*
wherethefactor/
max(0,1 \342\200\242
n) and max(0, r \342\200\242v)
1.Thecomputationactuallyuses/
positive and 0 otherwise to guard against the effects of negative dot products.
2. We consider the use oftreesin greater depth in Chapter 10.
and
light
is1 if1
\342\200\242
nis
Languages
453
454
Chapter 9
Shaders
Programmable
FIGURE 9.2
vector.
Expression trees, (a)
are assumed
For
Phong
shading,
reflection
environment
and can be
From basic data structures,
we know that arithmetic expressionsand
are equivalent Thus, wecan
use standard
tree-traversal algorithms for the evaluation
of expressions that define
shaders. Looking at shade trees slightly
we see that shade trees can be
differently,
that can be executedbygraphicsprocessors.
The
replacedbyprograms
programming
to developing shaders is supported
approach
by high-level
languages that we canuse
to program
the graphics pipeline on the latest graphics cards.
source parameters,
combined using
9.3
a set
of scalar
to
and
be available
vector
from
(b) For
the
operations.
trees
binary
EXTENDING OPENGL
into the details of programmable
which add a new level of
shaders,
getting
to examine
the mechanisms by which OpenGL
complexity to OpenGL, we pause
has evolved to incorporate advancesin graphics
hardware
and software. Graphics
such as OpenGL, developed as a way to p rovide application programmers with
APIs,
access
to hardware features that were being provided by the latest
hardware.
graphics
Before
9.3
As
and
mo re
and more hardware
features
became
re memory was provided on graphics
became
possible and even routine.
available, processor
as mo
techniques
programmers
Theproblem
through
expect such
would
processors,
only natural that application
supported by graphics APIs.
is how to support such
features
API developers must confront
API
while
not forcing programmers
that
an existing
speedsincreased,
complex graphics
It was
to be
techniques
more
Extending OpenGL
to replaceexisting
code.
One
add newfeatures
but arebackward
approach istoreleasenewversionsoftheAPIthat
compatible so that older code is guaranteed to run on newer versions. Anot her approach is t o add optional extensions to the API that allow application programs to
accessparticular
hardware
features. OpenGL has used both
these m echanism s. However,in order to access the new features of programmable graphics processors,a new
set of programming
tools is required.
9.3.1 OpenGL Versionsand
Extensions
API
has been very stable. Therewere
OpenGL 1.5that were released over a 10-year
period,each of which was compatible with previous releases. OpenGL 2.0, which
in 2004, was a major
was released
but still retains code compatibility
with
upgrade
earlier versions. The current
release
is OpenGL2.1.Thus,
written on an
any
program
older version of OpenGLruns as expected on a later version.
Changes to OpenGL
in hardware that became common to many
reflect
advances
graphics
processors.
For example, as hardware
for texture mapping increased, newer
versions
support
of OpenGL contained many
new functions
for using texture maps. Features
such as
texture objects, mipmapping, and three-dimensional
textures were added to the early
versions of Open GL.
Whereas
versions
users
and hardware
OpenGL
represent a consensus of many
card or high-end workstation
is likely to support feaproviders, a given graphics
tures
that
are not generally available
on other hardware. Such is especially the case
with commodity
cards that are used for computer games. Nevertheless, programmers
who
have access to a particular
of hardware want to be ableto access
piece
its features through OpenGL functions,
even
these features are not general
though
version ofth eAPI. One solution
to thisdilemma
enough to be supported
bythelatest
is to have an extension
mechanism
within OpenGL. Individual
hardware
providers
can provide accesst o (or expose) hardware features
new Open GL functions
through
that may only work on a particular
manufacturer's
hardware.
OpengGL extensions
have names that identify the provider. For example, an extension
with a name such
as glCommandNV
identifies it as provided by NVIDIA.
Other manufacturers
could
then
the same extension. Extensions
that have been widely
used
are apimplement
Review
Board (ARB) and are designated
as ARB
proved by the OpenGLArchitectural
One of
the main
upgrades from
features
OpenGL
of OpenGL
is that the
1.0 through
3. In 2006, the ARB w a s replaced by the
Seewww.opengl.org.
OpenGL
Working
Group under
the
Kronos
Consortium.
455
456
Ch a p te r
9
Shaders
Programmable
When programmable vertex shaders first became
the ARB approved
available,
for low-level
vertex p ro g ra ms . Later extensions
low-level fragsupported
ment programs. However,
the original
interfaces to programmable hardware
were
somewhat
for programmable
hardware was
unwieldy. The first software
support
much
like assembly language interfaces
for general
purpose computers. Application
had to write moderately long
that kept track of low-level
programmers
programs
and moving data bedetails, such as register assignments in the graphics
processor
tween
and memory. The OpenGL extensions provided
a mechanism
for
registers
loading this assembly-like code into a programmable
pipeline. Although this mechanism allowed users to work
with
shaders, it had all the faults of an
programmable
assemblylanguageprogram. AsGPUsbecame more sophisticated,itbecameincreasIn a manner similar
ingly more difficult to program shaders using
assemblylanguage.
to the development
of st an d ard programming languages,
higher-level
programming
interfaces and compilersfor shader code have now replaced assembly
language
p ro
extensions
for
gramming
most users.
GLSL and
9.3.2
Cg
to us are the
shading languages of most interest
(GLSL) a nd Cg, w hich is an acronym for Cfor Graphics.
Language
but have different targeted users. Both
are based on the C programming
include most ofits programming
constructsbut
add languagefeatures
that make it easier t o program
shaders.
two
The
high-level
The main differences
portable acrossmultiple
is virtually
arise
because
platforms,
Cg is
including
designed to
support
OpenGL Shading
They are
similar
and
language
and data
shaders
OpenGL and Microsoft's
types
that
are
DirectX.Cg
(HLSL) and thus
High LevelShading
Language
that
it
allows
them
to
advantage
developers
developshadersfor
both DirectX and OpenGL at the same time. However, the interface
between OpenGL
and Cg sh ader s is m ore sophisticated
than
the interface between OpenGLand GLSL
is. GLSL is part
of OpenGL
2.0 and thus is supported
by multiple vendors and on
multipleplatforms.
BecauseGLSLispart
ofOpe n GL, itis simpler todevelopOpenGL
shaders with GLSL than with Cg. Hence, wewill focus on GLSL, understanding that
the two approaches have far more
similarities
tha n differences.
has the
9.4
identical
to Microsoft's
for Windows
THE OPENGLSHADING
LANGUAGE
is a C-like language that
allows
the programmer
OpenGL Shading Language
both
vertex and fragment shaders. It is incorporated
into OpenGL 2.0. In
GLSL,there is little distinction in the syntax between a vertex program and a fragment program, although
the two are used in different
contexts.
Before we examine the GLSL lan guag e, first we examine the different tasks that these shaders must
The
to
write
perform.
9.4 The OpenGLShading
9.4.1 Vertex
A
vertex
Shaders
or vertex shader, replacesthe fixed-function
program,
the vertex processor with
defined in the
operations
operations
per-
shader. If a vertex
shader
is not provided by the application,
a programmable vertex processor carries
out
the standard
e rtex processor.A vertex
operations of the OpenGLfixed-functionv
shader is executed on each vertex
as i t passes down
the pipeline.
Every vertex shader
must
the information that the rasterizer
needs
to do its j ob . At a minimum,
output
must
for the rasterizer. For each vertex,
every vertex shader
output a vertex position
the input to the vertex
can use the vertex position
defined
program
by the application
and most of th e information
that is in the OpenGL state, including the
program
current
ma teri al properties,
and transformation matrices.
color, te xtu re coordinates,
In addition,
the application program can p ass other application-specific
information
on a per-vertex basisto the vertex
shader.
There are a few operations
that virtually every vertex program must
out.
carry
Recall that most application
programs
specify vertex positions in object
space, and
the vertex processor transforms
these
first by the model-viewmatrix
into
positions
matrix into clip coordinates.
Because
eye coordinates and then
by the projection
an application-supplied vertex shader replacesthefixed-functionv
e r tex operations,
one of the jobs that almost all vertex programs must carry out is to transform the
vertex
from object coordinatesto clip coordinates.
Because
the vertex
input
position
transformation
state, it has accessto the standard
program can accessthe OpenGL
matrices or it can compute its own transformations.
formed
Here
/*
pass
by
is a
simple,
but
vertex
through
complete,
shader
vertex
program:
*/
void main(void)
{
gl_Position = gl_ProjectionMatrix*(gl_ModelViewMatrix*gl_Vertex);
}
in object coordinates
shader simply takes each vertex'sposition
(gl_Vertex)
it
first
the
model-view
matrix
and
then
the
matrix
multiplies
by
by
projection
in
in the
to obtain the position
coordinates.
The
four
variables
(gl_Position)
clip
shader are all part of the OpenGL state and thus do not have to be declaredin the
shader. Each execution of g lV er t e x in an application triggers
the execution
of the
shader. This shader is so simple
that it does not even set a color
or any other vertex
such matters to the fragment
Because this shader does
attribute,
leaving
processor.
but
send
on
the
of
the
it
is
sometimes
calleda pass-through
vertex,
nothing
position
This
and
shader.
A slightly
follows:
more complexversi
on that
also assigns
a red colorto
each
vertex
is as
Language
457
458
C h a pt e r 9
Shaders
Programmable
/* simple vertex shader
*/
vec4 red = vec4(1.0,
const
0.0, 0.0, 1.0);/* C++
style
constructor
*/
void main(void)
{
gl_Position = gl_ModelViewProjectionMatrx*gl_Vertex;
= r ed ;
gl_FrontColor
}
This program does two things. It transforms the input
vertex
position
(gl_
of the projection and model-view
matrices
to a new
Vertex) by the concatenation
4
in clip coordinates (gl_Position)
and
colors
each vertex red. Because
position
the
conversion
of a vertex's position
from
object to clip coordinatesis so common,
GLSL
of the projection
and model-viewmatrices
provides the precomputed product
Notethatnames
variablegl_ModelViewProjectionMatrix.
refer to variables that are part of the OpenGLstate. Suppose
that
program that uses this vertex shader contains the code
throughthebuilt-in
that begin
the
with
gl_
application
glBegin(GL_P0LYGON);
glVertex3fv(v0);
glVertex3fv(vl);
glVertex3fv(v2);
glEndO;
in the application
have been defined previously
er t e x invokes our shader with a new value of
of the vertex in
gl_Vertex, which is the internal four-dimensional representation
h
Each
execution
oft
e
vertex
a
color
glVertex.
program outputs
(gl_FrontColor)
and
a new vertex position (gl_Posit ion) that
are passed on for primitive
assembly.
in Figure 9.3. In this shader, we could have
This process is illustrated
a
computed
color so that each vertex could be assigned a different
color.
We also could have set
in the application for each vertex using g lCol or and had the shader pass
the
color
on these colorsto the rasterizer by using the code
where
the
program.
vertices
vO, v l ,
and
v2
of glV
execution
Each
= gl_Color;
gl_FrontColor
has a main function
and can call
designation
The sameis t r u e for fragment
shaders.
GLSL includes new data types and the associated operations among them. Thus,
the
model-view
matrices are oftype
mat4
thevertex position
, whereas
andprojection
* is overloadedso that matrix-vector
isa vec4 datatype.
The multiplication
operator
in
the
other
shader.
Note that a
functions
4. We are assuming
because back faces
our shader should
written
that
vertex
shader
in GLSL.
two-sided lighting has not been enabled. If we need two-sided lighting
different
front and back properties, then
are visible and materials might have
also compute gl_BackColor.
9.4 The
OpenGL Shading
-Application
, program
glColor
glOrtho
glLoadMatrix
F r o n t Colo r
~\302\261
n
Positio
~
gl_Vertex
gl_
ModelViewMatrix
gl_ProjectionMartrix
9.3
FIGURE
Vertex
shader architecture.
as we
are defined
multiplications
would
Hence,
expect.
the code
in
our
simple
vertex
shader
gl_Position =
gl_ModelViewProjectionMatrix*gl_Vertex;
yieldsthefinalpositionthatisavec4datatype.
than
now, we have used the terms column matrix and row matrix rather
so as not to confuse the vector geometric type with its representation
using
row
and column
matrices. GLSL defines
a vector
data type that is a one-dimensional
C-style array. GLSL reserves the matrix data type for square matrices that are twoin this chapter.
dimensional C-style arrays.
We will use th e GLSLterminology
In our example,we usedbuilt-in
ion
variablesgl_FrontColor
andgl_Posit
for the output of the vertex program. Thefront colorisinterpolated
by the rasterizer
and the interpolated
colors are available to either
a fragment
program or the fixedfunction
for each vertex is used by the rasterizer
to
fragment processor. The position
for each fragment it produces. Note that
the red color has a const
assign a position
and will be the same for each
invocation
of the program. In general,
the
qualifier
of the vertex program.
output position and color can change with each invocation
Also
note the use of a C++-style
constructor
to initialize
the red colorvector.
Until
vector
9.4.2 Fragment Shaders
Fragment
ever,
shaders
written
programs
rather
than on
fragment
fragment
Considerour
in GLSL have the
are executed after
same
the
syntax
rasterizer
as vertex programs. Howand thus operate on each
each vertex.
in which the front
color
is passed onto primitive
If
the
vertex
is
not
eliminated
it goes on
assembly
stages.
by clipping,
to primitive
and
then
to
the
which
rasterizer,
assembly
generates fragments that
are then processed by the fragment
processor
using either fixed-function fragment
or an application-defined fragment
or fragment shader. Vertex
processing
program,
such as vertex colors and positions,
are interpolated
attributes,
by the rasterizer
In the simplest
across
a primitive to generate the corresponding
attributes.
fragment
trivial
and clipping
example
Language
4 59
460
Ch a p t e r9Pr
ogr
a m m a ble
Shaders
state
Vertices
Position
gl
g l FrontColor
.-
.
(per vertex)
9.4
FIGURE
(
gl_Fron t C o l or
olat e d )
g l _ Fragm
i n t er p
architecture.
processor uses theseattributes
fragment program is as follows:
/*
A minimal
modification.
without
shader */
fragment
pass-through
void
olor
r
Fragment shader
the fragment
case,
e n tC
mainO
\342\200\242C
gl_FragColor\342\200\242gl_Color;
}
as this
As trivial
a vertex
the
vertex
appears,
program
The values
function.
program
Rather,
program.
the
values
there is a major
of gl _Color
of gl_Color
difference
are
not
in the
between
the values
fragment
how it and
produced
program
by
have
interpolating the vertex values of gl_FrontColor
over
(and
gl_BackColor
lighting is enabled) from thevertex
processor
the primitive
to produce
the values used in the fragment
unless
all
Thus,
program.
the vertex colors are identical,
each tim e that the fragment
executes, it uses a
program
different
value
color produced by the fragment
ofg l _ C olor . The fragment
program
is then used to modify
the color of a pixel in theframe
buffer. Thisprocess
is shown in
the main features of GLSLand then develop some more
Figure 9.4. We now present
been produced
by
rasterizer
the
iftwo-sided
sophisticated
THE
9.5
The
shaders.
OPENGL
SHADING LANGUAGE
Shading Language is basedon the C programming
language. GLSL has
data
and
control
structures.
conventions,
However, because
naming
types,
and
shaders execute in a very
different
environment
than normal
fragment
OpenGL
similar
vertex
programs, including
significant
differences
the
from
OpenGL
C.
application
that invokes them, there
are some
9.5 The
9.5 .1 GLSL Execution
Let'sconsider
what
when a typical
happens
OpenGL
Most OpenGLfunctions
the OpenGL state
change
down the pipeline
flow
can
that
change
application
do not
but
the display.
However,
OpenGLShading Language
program executes.
cause anything
to
once
we execute any
OpenGL function that
the fixed-function
pipeline,
generates vertices\342\200\224the pipeline
into
action.
With
various calculations
are made
goes
in the pipeline to determine if the primitive
to which the vertex
is visible
belongs
These
calculations
and, if i t is, to color the corresponding
pixels in the frame buffer.
the OpenGL state. Subsequent
executions
of glV er t e x
generally do not change
if any state
invoke the same calculations
but use updated values of state
variables
in
have
been
made
between
the
calls
to
changes
glVertex.
If welook at these calculations as the work of the software and hardware
that
the
vertex
we
see
that
there
must
be
of
variables
implement
processor,
multiple types
involved in the execution of this code. Some variables will change
as the output
vertex attributes,
such
as the vertex color, are computed.
whose values are
Others,
determined by the OpenGL state, cannot be changed
by the calculation. Most internal
variables
must be initialized to their
values
after each vertex is processed
original
so that the calculation for the next vertex will be identical. Other variables must be
and passed
onto the next stage of the pipeline. Consequently,
when
we
computed
substitute a user-written
vertex
for thefixed-functionv
ert e x processor, we
program
function\342\200\224or
glVertex
mustbeableto
any
thesedifferent
identify
typesofvariables.
A fragment program also has input
internal
variables,
variables, and output
variables.
The major difference between
a fragment
program and a vertex
program
is that a fragment
will
be
executed
for
each
Some values will be
program
fragment.
in the shader. Others will
provided
by the OpenGLstate and thus cannot be changed
be changed
basis.
Most shader variables must
be
only on a fragment-by-fragment
initialized
for each fragment.
A typical
not only will change
state variables and entities
such
as
application
texture maps, but also may use multiple vertex and fragment
shaders. Hence, we also
must
examine
how t o load in shaders,
how t o link them into an GLSL program, and
how to set this GLSLprogram
to be the one that
uses. This process is not
OpenGL
but is accomplished
that are now
part of the GLSL language
using a set of functions
part of th e OpenGL core.
Because each vertex triggers an execution of the current vertex shader independent
of any other vertex and, likewise, each fragment
the execution of the
triggers
current
for extensive parallelism to speed the
shader, there is the potential
fragment
ofbo th vertices andfragments.
now contain
processing
High-end
graphicsprocessors
shader
cores that can executevertex
and fragment
shaders in parallel.
multiple
9.5 .2 Data
GLSL
are
int,
has
Types and Qualifiers
basic data
types
presently
limited to
and a
Booleantype,
C manner.
a
are
that
single
b o
to C
similar
floating-point
o l . Arrays
and
and
C++.
The scalar
types,
type, float,
asingle
structures
are declared in
integer
the
however,
type,
standard
4 6 1
462
Ch a p t e r
9
Progra
Shaders
mmable
for working
with the 2 x 2,
types
are special onecomputer graphics.Vectors
dimensional arrays.
We can use floating-point (vec2, vec3,vec4),
(ivec2,
integer
ivec3, ivec4), or Boolean(bvec2,bve c3, bvec4)
types. Because the vector types
can be usedto storevertex
can
colors, or texture coordinates,the elements
positions,
be indexed by position
coordinates
(x, y, z, w), color (r, g, b, a),ortexture
(s, t , p,
q).Thus,ifc is a vector, then c[1], c. y, c . g, and c.t allrefer tothesecondelement
of c. Note that thisflexibilityi
s there only to create more readablecodeand carries
no semantic information. Hence, there is n o reason that c. y cannot
contain color or
GLSLintroduces
3x3, and
and matrix
vector
new
4 matrices
4 x
that
we
use in
information.
texture
matrices in GLSL are
and floating-point
(mat2, mat3,
always
square
the usual
However,
OpenGL matrices, storage is by columns.
If m is a matrix,
m [1] is its second row
C/C++ referencing
and m [1] [2] is
applies.
in row 2, column 3. GLSLsupports
the
element
standard
C one-dimensional
arrays.
Multi-dimensional
can
be
obtained
structures in
array
functionality
using
C-type
Presendy,
mat4). As
other
with
GLSL.
GLSLusesC++-style
as in
the
vec3 a =
also
b =
vec2
uses
which
the vectors and
matrix
types,
such
.0);
vec3(1.0, -2.0,5
Constructors
to initialize
constructors
example:
following
can be
used
for
types as in
between
conversion
vec2(a);
w o componentsof the
thefirstt
be
vec3
variable
a to form
b.
those
ways. Ordinary (nonlocal)variables,
that are not function
or
function
can
be
as
atparameters
temporaries,
qualified
tribute,
uniform, varying, or const. The const qualifier is similar to but more
restrictive
than in C and makes the variable unchangeable by the shader. Thus, we
can create a constant
scalar and vector as follows:
Variables
float
const
can
qualified
in different
one = 1.0;
const vec3 origin =
2.0,3
vec2(1.0,
are used by
variables
Attribute-qualified
.0 ) ;
vertex shaders
for
variables
that change
once pervertex in the vertex shader. There aretwo types of attribute-qualified
variables. Thefirstt y p e includes the OpenGL state va riables that we associatewith
at most
vertex,
such as its
color,
as built-in
variables.
gl_Color are built-in
position,
texture coordinates,
and
normal.
These
a
are known
variables
and
gl_Vertex
Built-in variables need not be
in the apdeclared in shaders. GLSLallows
additional
vertex attributes to be defined
so
that
it
can
other
information
on
a
basis
to the
plication program
convey
per-vertex
shader.
For example, in a scientific
visualization
we mi gh t want to assoapplication,
ciate a scalar temperature
or a flow velocity
vector with each vertex. Only
floatingIn our
simple example, the
attribute-qualified
variables.
state
9.5
point
can be
types
declaredasin
attribute
attribute
Because
the
attribute
following
qualified.
User-defined
The OpenGL Shading
variables are
attribute-qualified
code:
float tei^perature;
vec3
velocity;
they vary on a vertex-by-vertex basis, vertex
shader. Attribute variables are aligned
a fragment
attributes
with
be declared
in the application
cannot
variables
in
a
through
OpenGL functions that we will explain in Section 9.6. When
shader is executed,the values of the attribute variables are those that were set
in the application
and cannot be changed in the shader.
Uniform qualifiers are used for variables
t hat are set in the application
program
for an entire batch of primitives; th at is, variables
whose
values are assigned outside
the
of a glBe gi n and a glEnd. Uniform variables
a mechanism for
scope
provide
data among
an application program, vertex
and fragment shaders.
shaders,
sharing
in a shader. Hence, we
Likeattribute
uniform
variables cannot be changed
variables,
useuniform
variables
t o pass information
that is constant over a batch
of primitives
into shaders. For example, we might
want
to compute the bounding
box of a set
of verticesthat define a primitive in the application
and send this information to a
shader
to simplify its calculations.
a var iabl e as a varying
variable
at first,
Although
naming
may seem a bit strange
program
vertex
for conveying data from
a vertex
varying-qualified variables provide the mechanism
shader to a fragment
shader.
These variables are defined
on a per-vertex basis but are
over the primitive
variables,
interpolated
by th e rasteri zer. As with attribute-qualified
variables
can be either built-in
or user defined.
varying
i p elin e .
how colors are determined in thefixed-functionp
Consider, for example,
Thevertex processor computes a color or shade for each vertex. The color of a fragment
is determined by interpolating the vertex
colors. Likewise, texture coordinates
for each
are determined by interpolating
texture coordinates at the vertices.
fragment
In our simple vertex
Both
vertex
colors and texture coordinatesarevarying
variables.
o l or is a built-in
variable. If we created a more complex
shader,
gl_FrontC
varying
vertex
shader
that computed a different
color
for each vertex, using
the varyingr
n
r
variable
o
t
C
ol
o
would
ensure
that
there
is
an
color
qualified
gl_F
interpolated
for each fragment, whether
or not we write our own fragment
program.
User-defined varying
variables
that are set in the vertex program are automatia varying
variable
cally interpolated
bythe rasterizer. It does not make sense to define
in a vertex shader and not use it in a fragment
shader.
Hence, a user-defined varyif
should appear in both the vertex and fragment
shaders.
ing variable
Consequendy,
we define such a variable in the vertex shader, we should
write
a corresponding
fragment shader. Thus, while our simple ve rt ex shader did not need the simple fragment
th e functionally
vertex shader is as follows:
shader,
equivalent
const
varying
vec4
vec4
red
= vec4(1.0,
color_out;
0.0, 0.0, 1.0);
/* varying
variable
*/
Language
463
464
C h a pt e r 9
Shaders
Programmable
void main(void)
i
gl_M o delVi
=
gl_Position
e w Pr o
jectio n Mat
r ix*gl_V
er te x
;
= red;
color_out
>
This
shader
vertex
at a
requires
the
minimum
pass-through
fragment
shader as fol-
lows:
vec4
varying
color_out;
void mainO
{
gl_FragColor= color_out
;
>
y
9.5 .3
For
and Functions
part, the operatorsareas in C with the same precedencerules.However,
internal format of thefloatan d integer types is not specified
by GLSL, bit
Operators
most
the
the
because
operations are not
The
operations
overloaded so that
example,
mat4
the
5
allowed.
among
the
matrix-vector
usual
C types
operators
and
the
can be
vector
and matrix
used as we
would
types are
expect.
For
code
a;
vec4 b,
c , d;
c =b*a;
d=a*b;
GLSL computes c treating
b as a row matrix,
whereas
a
column
matrix.
c
and
d
are
the
same
Hence,
computed treating
although
type, they will have different values.
GLSL has a swizzling
that is a variant
of the C selection operator (.).
operator
allows
us to select multiple
from the vector types. We can use
Swizzling
components
and write maskingto
select and rearrange
elements
ofv ect o r s . For example,
swizzling
we can change selected elements as in
sense.
makes
In
the first
d is
a =
vec4
a.x
a.yz
5.Bit
cas e,
b as
=
vec4(1.0 ,2
.0, 3.0, 1.0);
2.0;
=vec2(-1.0,
operations
are
4 .0);
supported
through
extensions on many GPUs.
9.6
or
swap
as in the
elements,
Linking
with
Shaders
code:
following
.yx;
a.xy=a
Notethatwecanuseanyoftheformats(x,y,z,w;r,g,b,a;s,t,p,q)aslongaswe
do not
mix
in singleselection.
them
GLSLhas many
functions
(sin,
including the trigonometric
and
mathematical
acos,
(asin,
atan),
trigonometric
functions
ticul ar importance are functions
(pow, log2, sqrt, abs, max, min).Ofpar
in computer
that help with geometric calculations
vectors that are required
involving
isa length function,
adistance
adot product
function,
graphics.Hence,there
anormalize
and a reflect
function.
Most
ofthese
functions
function,
function,
are overloadedso tha t they work with both floats and vectors. We will see examples
of these and other built-in functions in the examples.We will delay our discussion of
the
texture
functions
until Section 9.10.
cos, t an),
built-in
functions
functions
inverse
Variables
that are function parameters necessitatea few additional
options. Because ofhowvertexandfragment
shadersare
GLSL
executedandthelackofpointers,
uses a mechanism
known
as callbyvalue-return.
Function parameters are qualified
as in (the default), out,
or inout.
GLSL functions have return
Returned
varitypes.
ables are copiedback to the calling function. Input parameters
are copied from the
If a variable is qualified
as in, it is copied in but is not copied out,
calling
program.
even though
its value may be changed
within
the function. A function
that
parameter
is qualified
as o u t is undefined
on entry to the function
but can be set in the function and is copied back to the calling function. A parameter that is inout-qualified
is copied in and also copied out.
in GLSL. Because vectors and
Note that there are presently no pointers
matrices
are basic types, they can be copied into and copied
from functions.
9.6
LINKING SHADERS WITH
With GLSL, we can write
development
shaders
exist
environments
OPENGL
PROGRAMS
of any OpenGL
independent
that let users write
and test
Various
application.
shaders. However, at
application.
how to create vertex and
point, we have t o link the shaders with the OpenGL
There is a set of OpenGL
functions
that deals with
shader
an OpenGL
objects, link them with
application, and enable the
variables among the OpenGL
and the shaders.
program
Thereare usually eight steps in initializing
one or more shaders in
some
tion:
1. Read t he
shader
source.
2. Create a program
object.
3. Create
shader objects.
4. Attach
th e
5. Compile
the
shader objects to
shaders.
the
program
object.
passing
the
of
applica-
OpenGL
Programs
465
466
C h a p te r
9
Shaders
Programmable
6.
Link
together.
everything
7. Select current
object.
program
and
8. Align uniform
the application
between
variables
attribute
the
and
shaders.
Usually, our shaders
as null-terminated
in files
stored
be
will
common
most
The
application.
shaders are
code
The following
strings.
as sourcecodethat
the
that
way
afilea
read
will
will
used
then
nd
be read by
by OpenGL
create the
the
is
desired
string:
<stdio.h>
#include
static char* readShaderSource(const
char*
{
/*
shaderFile)
reader */
shader
/* creates null
FILE* fp =
char*
buf;
long
size;
terminated
from
string
*/
file
fopen(shaderFile, \"r\;
return(NULL);
if(fp==NULL)
OL, SEEK.END);/*
fseek(fp,
size =ftell(fp);
/*
fseek(fp, OL,SEEK_SET);
/*
go to
go to
+ 1)
= (char*) malloc((size
1 , size,
fread(buf,
fp);
buf
end
size
get
of
of
file
file
beginning
*
*/
*/
of file
*/
sizeof(char));
buf[size] = ' ';
fclose(fp);
return
buf;
Cfilef u n c ti on s
uses the
Thiscode
n t o the
and readsthefilei
Suppose that
to obtain
string,finallyt
two shaderfilesa
our
thesize ofthefilei topens,allocates
e r min a t in g
re given
itwith
as follows:
GLchar vShaderFile[]
=
GLchar
= \"myFragmentShader.glsl\";
fShaderFile[]
We can read the
shaders
GLchar *vSource,
\"myVertexShader.glsl\";
and check
if
thefilese
x i st as follows:
*fSource;
vSource= readShaderSource(vShaderFile);
if(vSource
\342\200\242C
==
NULL)
a null
character.
space
9.6
printf(\"failed
to
vertex
read
Shaders
Linking
with
shader\;
exit(EXIT_FAILURE);
}
fSource = readShaderSource(fShaderFile);
if(fsource ==
NULL)
{
to read
printf(\"failed
shader\;
fragment
exit(EXIT_FAILURE);
}
We now can
GLunit
create a program
and get
object
a name (or identifier)
for
it:
myProgObj;
=
myProgObj
glCreateProgramO;
The program object is a container
functions. We createour shader
GLuint
vShader,
that
objects
multiple shaders
similar way as follows:
can hold
in a
and
GLSL
other
fShader;
vShader
= glCreateShader(GL_VERTEX_SHADER);
fShader
= glCreateShader(GL_FRAGMENT_SHADER);
We can add these shadersto our
program
glAttachShader(program,
vShader);
glAttachShader(program,
fShader);
object
by attaching them
as follows:
Note that at this point the actual shader code has not been associated
with
gram object.We create this association as follows:
glShauerSource(vShader,
1,
glShaderSource(fShader,
1,
(const
(const
GLchar**)
GLchar**)
fevSource,
&fSource,
the
pr o-
NULL);
NULL);
of the shader object into
which
the source will
parameter is the name
The second parameter is the number
of string buffers from which
the
shader source will be loaded. In our example,we have read the source into a single
buffer
with our ReadShaderSource
function.
The third parameter
is a pointer
to
The
first
be loaded.
an array
of pointers
to the string buffers, which
for our example is simply
a pointer
usedwithReadShaderSource.
TheNULLfor
thefourth
arraysthatwe
indicates that the stri ngs are null terminated. Alternately,
it could point to
of buffer
values. We can now compile
the shaders as follows:
length
tooursource
parameter
an
array
glCompileShader(vShader);
glCompileShader(fShader);
At this
that
point,
it compiled
to make sure that there
without errors. The function
we want
are no
errors
in
our
shader
code and
OpenGL
Programs
4 67
468
C h a pt e r 9
Shaders
Programmable
static void
c on s t
status,
checkError(GLint
char
*msg)
{
if
!=GL_TRUE)
(status
{
printf(\"7.s\",msg);
exit(EXIT_FAILURE);
}
>
will write
a messageand
get the status
parameters.In
through
exit
the query
glGetShaderiv(vShader,
glGetShaderiv(fShader,
checkError(status,
Now we
can link
\"Failed
status and
together
everything
exit
ftstatus);
to compile the
GL_COMPILE_STATUS,
to
compile
6
GLJTRUE. We
which returns shader
if is not GLJTRUE:
is not
variable
glGetShaderiv,
e compile
GL_COMPILE_STATUS,
\"Failed
checkError(status,
function
check th
case, we
this
the status
whenever
vertex shader.\;
ftstatus);
the
fragment
shader.
\"
);
as follows:
glLinkProgram(myProgObj);
Because
use.For
the
we can
program
create
program
multiple
object that we just
created,
objects, we m u s t identify
we execute the function
which one to
glUseProgram(myProgObj);
asfollows:
and checkitsstatususingglGetProgramiv
glGetProgramiv(program, GL_LINK_STATUS,
checkError(status,
\"
Failed
to
link
festatus);
the shader
program
object.\;
The error checking we used for compiling
and linking does not give the information we needifth er e are any errors. We can get more detailbyusing
the functions
and glGetShaderInfoLog for shaders and glGetProgramiv
glGetShaderiv
and glGetProgramlnf oLogfor the program object. The mechanismis similar
for
shader and program objects.We use glGetShaderiv
or glGetProgramiv
to deif there has been an error
termine
and the length of the error
message
string. We
canthen gettheerror stringsusingglGetShaderInf oL og andglGetProgramlnf oLog. The sample
in Appendix A uses thesefunctions.
program
in the shaders with
in the proThe next step is associating
variables
variables
in
in
Vertex
attributes
the
shader
are
indexed
the
main
gram.
program through tables
6.For
for
general OpenGL functions, we can query
the returned status GL_NQ_ERRQR.
for errors
with the function
glGetError
and check
9.6
Linking
w i th OpenGL
Shaders
are set up during linking.
We obtain
the needed indices through
the function
These indices arelater used to obtain values computed in
glGetAttribLocation.
the shader using the various
forms
of the function glVertexAttrib.
For example,
supposethat we set an RGBA color in the shader in a variable named myColor. We
get its index as follows:
that
GLuint
colorAttr;
colorAttr = glGetAttribLocation(myProgObj,
we can
Later,
use itsvaluein
the
\"myColor\;
as
OpenGLprogram
GLfloat color[4];
color);
glVertexAttrib4fv(colorAttr,
set the
will
which
A similar
variable
form
in the
of myColor
value
initial
shader.
vertex
Suppose that we compute a uniand want to send it to the shader. We get an
process holds for uniform
a ngle in the application
variables.
index
GLint
angleParam;
angleParam =
and later
can compute a value
GLfloat
my_angle;
= 5.0;
my_angle
and send
it
to the
/* or
and
version
GLfloat
other
some
program
application
*/
value
by
my_angle);
glUnif orm has one- to
can b e used to set scalarand
red[4]
GLint
in the
the shaders
glUniformlf(angleParam,
The function
\"angle\;
glGetUniformLocationCmyProgObj,
vector
a pointer
and
For example,
values.
the
(v)
code
0.0, 0.0, 1.0};
={1.0,
ColorParam;
\"
= glGetUniformLocation(myProg0bj,
red)
glUniform4fv(colorParam,
colorParam
sets the
nif
forms
four-dimensional
uniform
ormMatrix
shader
variable redColor.
are u se d
to set uniform
value of a uniform
redColor\
The various
matrices
forms
of the function
glU-
in shaders.
We can query the
variable
from a shader through
the functions glGetUnif ormlfv and glUnif
ormliv.
variables
However, because uniform
cannot be changed in the shader, we rarely need these functions
and will not discuss
them
further.
Programs
4 69
470
C h a pt e r 9
Shaders
Programmable
functions here, most of them are
n o t change much from
does
only
usage
application to application. Ifw e look at what we have to do in a typical application program,
link them to program
there
are two basic parts. First,
we set up various shaders and
objects.This part can be put in an initialization function in the OpenGL program.
we either get values from
Second,
during the execution of the OpenGL
program,
shaders
or send values to shaders.Usually,
we do these operations where we would
set valuesfor colors, texture
and other program varicoordinates,
normals,
normally
ableswith an application that uses thefixed-functionp ip elin e .
A contains a full program
with a basic vertex shader and a basic fragAppendix
ment
shader. Next, we focus on what
we can do with vertex shaders.
there
Although
used
is a
fair
9.7
of OpenGL
number
dur i ng initialization
and
their
VERTICES
MOVING
develop someexamplesofvertex shaders.Thefirst
examplesinvolve
moving
that
is p a r t of many animation
strategies. In each case, we must
because
the execution
generate an initial vertex position in the OpenGL program
In
of the function
initiates
the
vertex
shader.
these
first
glVertex
examples, the
work
done by the vertex shaders could have been done in your
application
program.
However, we often want to do this work in the vertex shader because it frees up the
CPU for other tasks. The second set of examplesinvolves
vertex col ors.
determining
We will see that by writing our own vertex shaders we gain more control over the
vertex
and transfer much of the computation
from the CPU to the GPU.
processing
We now
vertices, something
9.7 .1
Scaling
Vertex Positions
vertex
program that changes thelocation
we must remember that once we
use a vertex program of our own, we must do all the functions
of the fixed-function
In particular, we are responsiblefor converting
vertex
vertex locations
processor.
ofthe
One
of each
simplest examplesofa
vertex that triggers the
shader.
is a
shader
However,
from object coordinatesto clip coordinates.In our simplest examples, we can do
this
matrices
that are part of the
operation
by using th e model-viewand projection
we could compute the clip spaceposition
of each vertex
OpenGL state. Alternately,
in the shader
without
using the built-in matrices.
In our first example, we scale eachvertex
so that the object appears to expand
and
contract.
The scale factor varies sinusoidally,
based
on a time parameter that is
in
if
as
a
un
orm
variable
from
the
as follows:
passed
OpenGL
program
/*
vertex
uniform
shader
float
void mainO
{
float
s;
that
time;
moves
vertex
locations
/* value provided
by
sinusoidally
application
program
*/
*/
9.7
s = 1.0
+0.5*sin(time);
s,
=gl_ModelViewProjectionMatrix*(vec4(s,
gl_Position
s,
1 .0)
*gl_Vertex);
>
model-view and projection
matrices
from the
the scalefactor only to the first three components
of the vector (see Exercise 9.19). By applying
the scaling to gl_Vertex
before the
vertex
is transformed by the projection
and model-view
is done
matrices, t he scaling
in object coordinates rather
in clip coordinates. In the application
than
program, we
set up the time variable as follows:
we use
that
Note
OpenGL
GLuint
and
state
the
of the
product
that we apply
timeParameter;
=
timeParam
callback can either
The idle
\"time\;
glGetUniformLocationGnyProgObj,
the time
increment
or use the
elapsed
time in
glUnif orm
as follows:
IdleO
void
{
glUniformlf(timeParam,
glutGet(GLUT_ELAPSED_TIME));
glutPostRedisplayO;
}
Note that elapsed time is in milliseconds,
so we probably want to scale it in the shader
the scale factor in the shader. For example, if we usethe line
changing
by
1.0 +
s =
0.5*sin(0.001*time);
is converted
time
We can make
depend
to seconds.
this
example
somewhat
position of eachvertex.
value
of each vertex is
on the
which the
y
Section 5.7, we displayed
of the form
such
more
Suppose
the
data with
height
a display
if we let the variation
we start with a height field in
In
and we change only
this value.
callback
or idle callback using
code
interesting
that
int. i.j;
glBegin(GL_QUADS)
for(i=0;
i
i++) for(j=0;
j
{
glVertex3f((float)i/N, data[i][j], (float)j/N);
glVertex3f((float)i/N,
glVertex3f((float)(i+l)/N,
data[i][j],
(float)(j+l)/N);
data[i][j],
glVertex3f((float)(i+l)/N, data[i][j],
}
glEndO;
(float)(j+l)/N);
(floa t)(j)/N);
Moving
Vertices
47 1
472
Ch a p te r
9
Shaders
Programmable
where the height data is in the array d a t a and x and z are defined
(0,1). We can vary the heights with the vertex shader as follows:
/*
program for
vertex
float
uniform
vo:id
{
a height
varying
field
float
s=
float
xf
*/
time;
yf
5.0;
// x
=
5.0;
//
=0 . 1 / /
t =gl_Vertex;
factor
scale
0.001; //time
=
h
vec4
frequency
z frequency
scale
height
t.y =gl_Vertex.y +h*sin(s
*time
+
xf*gl_Vertex.x)
+ zf*gl_Vertex.z);
sin(s*time
gl_Position =gl_ModelViewProjectionMatrix
>
that
Note
y component
is a built-in
uniform
gl_Vertex
in the shader. We createa temporary
t;
we cannot change
variable,
variable t that is initial-
because
and canbe
asa copyofgl_Vertex
9.7.2
*
= gl_Color;
gl_FrontColor
ized
the range
mainO
float
float
its
over
shader
altered.
Morphing
in animation
is known as keyframing.In this techin
of
our
scene at a set of times
or in a set
positions
objects
of positions, thus defining the key frames. We then interpolate
between
successive
frames or positions
to get the in-between frames or positions.
in which we smoothly change
One
variant
of this idea is morphing, a technique
one object into another. One way to accomplishthis change is to have the set of
vertices that define one objectchange
t h e i r locations (and
other
to those
attributes)
ofth
e other object. Let's look at a simpleexamplewhere
we have the same number of
standard
of the
One
nique, we
define
vertices in
two
techniques
the
arrays.
Suppose that we have
The first specifies object A and the
want to morph object A into object B, we
of vertices
and that corresponding vertices
are matched.
vertex k in the first array of vertices should morph into vertex k
Thus,
in the second array. In general, these sets are formed
by the animator with the aid of
software
that can create extra verticest o ensure
that the two sets are of th e same size
two
sets
of vertices.
second specifiesobjectB as polygons.
assumethat they have the same number
with
another
matching
vertices.
two-dimensional
Figure 9.5
polygon.
a two-dimensional
shows
The vertex shader needsto output
between
two vertices provided
is that we can pass in only a single
However, we can pass in
gl_Vertex.
lating
If w e
asinglevertex
that is
polygon morphing
into
constructed byinterpo-
program. The main problem
vertex
the built-in uniform
variable
through
the corresponding
vertex using an applicationby
the
OpenGL
9v7
(b)
M
9.5
FIGURE
B. (c)
(c)
Morphing.(a)ObjectA.(b)
A 2/3 morphed to object
Object
vertex attribute.
much of eachvertex's
denned
We
location
Id)
Object A 1/3morphed to
B. (d)
also pass in
we should
a uniform
use in
object
B.
Object
determines how
Here is a vertex
that
variable
the
interpolation.
shader:
vec4 vertices2;
attribute
float
uniform
blend;
void main()
{
vec4 t =vec4(mix(gl_Vertex.xyz,
gl_Position = gl_ModelViewProjectionMatrix*t;
blend),
vertices2.xyz,
1 .0);
\342\200\242
gl_FrontColor
gl_C61or;
}
The GLSL function
mix
forms
the
affine
forexample,
component
ofmix(gl_Vertex,
blend)*gl_Vertex.x +blend*vertices2.x .
can define
as follows:
GLuint
the
vertices2,
vertices2Parant
In
vertex attribute v er t i c e s 2
mydisplayO
parameter
blend\;
\"
=glGetAttribLocation(program,
callback,we should
see code
vertices2\;
something like the
N50 /*number of vertices*/
GLfloat vertices_two[N][3], vertices[N][3];
void
uniform
\"
glGetllniformLocation(program,
the display
fdefine
the
and
vertices2Param;
blendParam,
blendParam =
Within
required
two vertices. Thus,
blend) is(1application
program, we
of the
combination
thefirst
following:
blend
Moving
Vertices
473
474
C h a pt e
r 9
Shaders
Programmable
{
blend =
;
value
set
/*
of
blend
*/
glBegin(GL_TRIANGLES)
ford
i< N; i++)
=0;
-C
blend);
glUniform(blendParam,
/*
set color and other vertex attributes
*/
glVertexAttrib3fv(vertices2Param_location, vertices_two[i]);
}
glVertex3fv(vertices[i]);
glEndO;
glutSwapBuffersO;
}
We probably want
coordinates, and
to embellish
vertex
other
this program quite
a bit by ble nding
(see Exercise 9.1).
colors,
texture
attributes
9.7 .3 ParticleSystems
Particle
provide
systems
plex real-world objects,
of many
one of
such
the
most
as clouds,
important
trees, water,
approaches
and
hair.
to
Particle
simulating
com-
systems are one
used in computer graphics, a topic to which
we will reidea in all particle systems is that
we can model the
in space either using realistic
motion of points
or by creating our own physphysics
ics. For each time step, we determine a new location
for each particle. We can then
For example, if we want to create
display
any object that we desire at these locations.
use a collection of translucent
each of which would be
clouds, we might
polygons,
centeredat the location of a particle. For a pieceof cloth, we can start with an array
oflocationsthatdeterminea
mesh, eachofwhoseverticesis aparticlewhoselocation
methods
procedural
12. The basic
turn in Chapter
time.
with
changes
for particle systems. One reasonis that we can have
work involved in positioning
each particle much faster
than
the CPU can. Becausevertex
shaders
cannot
create new vertices the OpenGL
at a minimum,
must
so as t o trigger
the
program,
generate each particle as a vertex
vertex shader. A particularly
but one that can be extended easily
simple
example,
Vertex shaders
the shader do
work
much
well
of the
(Exercise 9.2),is to generateparticlesthat are subject only to the forces of g rav ity .
Consider
an ideal point
to Newton's laws. Supposethat it has an initial
subject
such
(XQ, y0, ZQ), and an initial
(vx, vy, vz). If we ignore effects
position
velocity
constant g, its position
at time t is given
friction, using the gravitational
by
x(t)=x0
y(0=y0+
z(t)=z0
+vxt,
y
+vgt.
Sf2
+*'.
2
as
9.8 Vertex
Thus, we can
the
that
vertex. Because the
shad er
vertex
variableis
updated bythe
shader computes new
for
such
a particle
each
application,
vertex
positions.
time through the
Here is a simple
system:
vec3 vel;
attribute
float
uniform
time
the vertex
callback,
display
and eachparticle'sinitial velocityto
the shader as uniform
Each time
particles'sinitial position through gl_Vertex.
is executed, it then computes the present position
of the
the time
pass
and pass in the
vertex program
variables
Lighting
time,
void mainO
g;
{
vec4
= gl_Vertex;
temp_position
temp_position.x
=temp_position.x +vel.x*time;
temp_position.y
=temp_position.y
=
gl_Position
+ g/(2.0)*time*time;
+vel.y*time
temp_position.z =temp_position.z
+ vel.z*time;
*
gl_ModelViewProjectionMatrix
Here we use attribute-qualified
shader because we may use
variables
to pass
and glEndO
single glBegin(GL_POINTS)
The time and gravity
velocity.
are
per particle
callback that
a display
assumed
and
to be
temp_position);
information
all the
generates
particles
particle may have a
same for all particles.
each
the
We can extend this example in many ways. One simple
createwith this shader is to simulatefireworksb
each
yhaving
to
within
the
a
different
that we can
vertex render as a point
for each particle
position
the
shader
with the same initial
a slightly
different
initial velocity. In addition, we can change
the
color
as time progresses and even make particles disappear (see Exercise
9.3).
Ifth
e vertices are part of a more
the entire object is
complex
object than a point,
now
to gravity. One interesting
va ri ant is to create a bouncing
effect by using
subject
ther e flect function
thevertexlocation
tochangethevelocitywhen
hitstheground
(see Exercise9.5).
and
sending
but
giving
9.8
each vertex
Perhaps
the
WITH
LIGHTING
VERTEX
models
and
each vertex to
application
most
SHADERS
use of vertex shaders is to generate alternate
lighting
odified Phong model. We start by writing the Phong
models
as vertex shaders. We do so
(Blinn-Phong) lighting
the structure and functions
used to compute lighting
with
a
important
to thefixed-functionm
modified
primarily
Phong
to introduce
vertex shader. Then we will
examine
alternate
lighting methods.
9.8.1 Phong Lighting
The
for
modified
Phong lighting model that we developed
a front-facing surface, without
the distance
terms,
/=kdLd\\
\342\200\242n+
^I,(n
+ kttLaIa.
\342\200\242h)a
in Chapter
as
6 can be written
with
Shaders
4 75