Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 5 of 5

Thread: How to shade a grid of quads

  1. #1
    Junior Member Newbie kevinfishburne's Avatar
    Join Date
    May 2012
    Location
    Stone Mountain, GA, USA
    Posts
    25

    How to shade a grid of quads

    I am rendering a grid of quads to represent a section of a landscape. Each quad has its own separate texture (required) and elevation. I use an averaging algorithm so the vertices of a given quad line up with those of its eight adjacent quads (imperfect but fix is known). How can I shade the grid of quads with respect to an OpenGL light? My code looks like this:

    Code :
    Public Sub Tile_Grid()
     
      ' Draw tile grid texture array in render window.
     
      ' General declarations.
      Dim TileGridX As Short      ' Tile in tile grid texture array being rendered.
      Dim TileGridY As Short      ' Tile in tile grid texture array being rendered.
      Dim TileGridX1 As Short     ' Starting tile in tile grid texture array to be rendered.
      Dim TileGridY1 As Short     ' Starting tile in tile grid texture array to be rendered.
      Dim TileGridX2 As Short     ' Ending tile in tile grid texture array to be rendered.
      Dim TileGridY2 As Short     ' Ending tile in tile grid texture array to be rendered.
      Dim OffsetX As Short        ' Tile offset relative to tile grid position.
      Dim OffsetY As Short        ' Tile offset relative to tile grid position.
      Dim StepX As Short          ' Direction to step through tile grid texture array loop.
      Dim StepY As Short          ' Direction to step through tile grid texture array loop.
      Dim InverseX As Single
      Dim InverseY As Single
      Dim ScreenOffsetX As Single
      Dim ScreenOffsetY As Single
      Dim Elevation As Single
      Dim Elevation1 As Single
      Dim Elevation2 As Single
      Dim Elevation3 As Single
      Dim Elevation4 As Single
      Dim Elevation5 As Single
      Dim Elevation6 As Single
      Dim Elevation7 As Single
      Dim Elevation8 As Single
      Dim Elevation9 As Single
      Dim Counter As Single
     
      ' Assign initial values to variables.
      TileGridX1 = TileGrid.CenterX
      TileGridY1 = TileGrid.CenterY
      TileGridX2 = TileGrid.CenterX + TileGrid.Size - 1
      TileGridY2 = TileGrid.CenterY + TileGrid.Size - 1
      If TileGridX1 < TileGridX2 Then
        StepX = 1
      Else
        StepX = -1
      Endif
      If TileGridY1 < TileGridY2 Then
        StepY = 1
      Else
        StepY = -1
      Endif
     
      ' Enable depth testing.
      Gl.Enable(Gl.DEPTH_TEST)
     
      ' Render tile grid texture array.
      For TileGridY = TileGridY1 To TileGridY2 Step StepY
        For TileGridX = TileGridX1 To TileGridX2 Step StepX
          ' Calculate diagonally bordering tiles' elevations.
          Elevation1 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX - 1, Camera.CellGridY - TileGrid.Offset + OffsetY - 1] / 12 * ElevationScale
          Elevation2 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 0, Camera.CellGridY - TileGrid.Offset + OffsetY - 1] / 12 * ElevationScale
          Elevation3 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 1, Camera.CellGridY - TileGrid.Offset + OffsetY - 1] / 12 * ElevationScale
          Elevation4 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX - 1, Camera.CellGridY - TileGrid.Offset + OffsetY + 0] / 12 * ElevationScale
          Elevation5 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 0, Camera.CellGridY - TileGrid.Offset + OffsetY + 0] / 12 * ElevationScale
          Elevation6 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 1, Camera.CellGridY - TileGrid.Offset + OffsetY + 0] / 12 * ElevationScale
          Elevation7 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX - 1, Camera.CellGridY - TileGrid.Offset + OffsetY + 1] / 12 * ElevationScale
          Elevation8 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 0, Camera.CellGridY - TileGrid.Offset + OffsetY + 1] / 12 * ElevationScale
          Elevation9 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 1, Camera.CellGridY - TileGrid.Offset + OffsetY + 1] / 12 * ElevationScale
          ' Select texture using its ID.
          Gl.BindTexture(Gl.TEXTURE_2D, tTileGrid[Convert.Wrap_Short(0, TileGrid.Size - 1, TileGridX), Convert.Wrap_Short(0, TileGrid.Size - 1, TileGridY)][0])
          ' Create the quad the texture is drawn on.
          Gl.Begin(Gl.QUADS)
            ' Top-left vertex.
            Gl.TexCoord2i(0, 0)
            Gl.Vertex3f(TileGrid.WorldX + OffsetX, TileGrid.WorldY + OffsetY, (Elevation1 + Elevation2 + Elevation4 + Elevation5) / 4)
            ' Top-right vertex.
            Gl.TexCoord2i(1, 0)
            Gl.Vertex3f(TileGrid.WorldX + OffsetX + 1, TileGrid.WorldY + OffsetY, (Elevation2 + Elevation3 + Elevation6 + Elevation5) / 4)
            ' Bottom-right vertex.
            Gl.TexCoord2i(1, 1)
            Gl.Vertex3f(TileGrid.WorldX + OffsetX + 1, TileGrid.WorldY + OffsetY + 1, (Elevation6 + Elevation8 + Elevation9 + Elevation5) / 4)
            ' Bottom-left vertex.
            Gl.TexCoord2i(0, 1)
            Gl.Vertex3f(TileGrid.WorldX + OffsetX, TileGrid.WorldY + OffsetY + 1, (Elevation4 + Elevation7 + Elevation8 + Elevation5) / 4)
          Gl.End()
          ' Adjust tile position.
          OffsetX = OffsetX + 1
          If OffsetX = TileGrid.Size Then OffsetX = 0
        Next
        ' Adjust tile positions.
        OffsetX = 0
        OffsetY = OffsetY + 1
        If OffsetY = TileGrid.Size Then OffsetY = 0
      Next
     
    End

    The render looks like this:

    http://youtu.be/vFan5CKB98c

    This is a solo project, so the "simplest" way is preferred to the "right" way unfortunately. Any insight will be greatly appreciated!

  2. #2
    Super Moderator OpenGL Guru
    Join Date
    Feb 2000
    Location
    Montreal, Canada
    Posts
    4,256
    Set your texture environment mode to GL_MODULATE. See glTexEnvi.
    Set the material states for your quad. See docs for glMaterialf.
    http://www.opengl.org/sdk/docs/man/
    ------------------------------
    Sig: http://glhlib.sourceforge.net
    an open source GLU replacement library. Much more modern than GLU.
    float matrix[16], inverse_matrix[16];
    glhLoadIdentityf2(matrix);
    glhTranslatef2(matrix, 0.0, 0.0, 5.0);
    glhRotateAboutXf2(matrix, angleInRadians);
    glhScalef2(matrix, 1.0, 1.0, -1.0);
    glhQuickInvertMatrixf2(matrix, inverse_matrix);
    glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
    glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);

  3. #3
    Junior Member Newbie kevinfishburne's Avatar
    Join Date
    May 2012
    Location
    Stone Mountain, GA, USA
    Posts
    25
    I made some progress in that I created a function to calculate the surface normal of a triangle:

    Code :
    Public Function Normal(p1 As Single[], p2 As Single[], p3 As Single[]) As Single[]
     
      ' Calculate and return surface normal of specified triangle.
     
      ' General declarations.
      Dim U As New Single[3]
      Dim V As New Single[3]
      Dim N As New Single[3]
     
      ' Calculate normal.
      U[0] = p2[0] - p1[0]
      U[1] = p2[1] - p1[1]
      U[2] = p2[2] - p1[2]
      V[0] = p3[0] - p1[0]
      V[1] = p3[1] - p1[1]
      V[2] = p3[2] - p1[2]
      N[0] = U[1] * V[2] - U[2] * V[1]
      N[1] = U[2] * V[0] - U[0] * V[2]
      N[2] = U[0] * V[1] - U[1] * V[0]
      Return N
     
    End

    I also followed your advice as best I could and burned through several dozen tutorials and reference docs to little avail. I have this in my init code:

    Code :
      ' Set texture environment mode.
      Gl.TexEnvi(Gl.TEXTURE_ENV, Gl.TEXTURE_ENV_MODE, Gl.MODULATE)
     
      ' Enable backface culling.
      Gl.Enable(Gl.CULL_FACE)
     
      ' Set shading model.
      Gl.ShadeModel(Gl.SMOOTH)
     
      ' Enable lighting.
      Gl.Enable(Gl.LIGHTING)
      Gl.Enable(Gl.LIGHT0)
      Gl.Enable(Gl.COLOR_MATERIAL)

    I updated the tile grid render procedure to look like this:

    Code :
    Public Sub Tile_Grid()
     
      ' Draw tile grid texture array in render window.
     
      ' General declarations.
      Dim TileGridX As Short      ' Tile in tile grid texture array being rendered.
      Dim TileGridY As Short      ' Tile in tile grid texture array being rendered.
      Dim TileGridX1 As Short     ' Starting tile in tile grid texture array to be rendered.
      Dim TileGridY1 As Short     ' Starting tile in tile grid texture array to be rendered.
      Dim TileGridX2 As Short     ' Ending tile in tile grid texture array to be rendered.
      Dim TileGridY2 As Short     ' Ending tile in tile grid texture array to be rendered.
      Dim OffsetX As Short        ' Tile offset relative to tile grid position.
      Dim OffsetY As Short        ' Tile offset relative to tile grid position.
      Dim StepX As Short          ' Direction to step through tile grid texture array loop.
      Dim StepY As Short          ' Direction to step through tile grid texture array loop.
      Dim InverseX As Single
      Dim InverseY As Single
      Dim ScreenOffsetX As Single
      Dim ScreenOffsetY As Single
      Dim Elevation As Single
      Dim Elevation1 As Single
      Dim Elevation2 As Single
      Dim Elevation3 As Single
      Dim Elevation4 As Single
      Dim Elevation5 As Single
      Dim Elevation6 As Single
      Dim Elevation7 As Single
      Dim Elevation8 As Single
      Dim Elevation9 As Single
      Dim TopLeft As New Single[3]
      Dim TopRight As New Single[3]
      Dim BottomRight As New Single[3]
      Dim BottomLeft As New Single[3]
      Dim Counter As Single
     
      ' Assign initial values to variables.
      TileGridX1 = TileGrid.CenterX
      TileGridY1 = TileGrid.CenterY
      TileGridX2 = TileGrid.CenterX + TileGrid.Size - 1
      TileGridY2 = TileGrid.CenterY + TileGrid.Size - 1
      If TileGridX1 < TileGridX2 Then
        StepX = 1
      Else
        StepX = -1
      Endif
      If TileGridY1 < TileGridY2 Then
        StepY = 1
      Else
        StepY = -1
      Endif
     
      ' Enable depth testing.
      Gl.Enable(Gl.DEPTH_TEST)
     
      ' Set material properties.
      Gl.ColorMaterial(Gl.FRONT, Gl.DIFFUSE)
      Gl.Materialf(Gl.FRONT, Gl.AMBIENT, 0)
      Gl.Materialf(Gl.FRONT, Gl.SPECULAR, 0)
     
      ' Render tile grid texture array.
      For TileGridY = TileGridY1 To TileGridY2 Step StepY
     
        For TileGridX = TileGridX1 To TileGridX2 Step StepX
     
          ' Calculate adjacent tiles' elevations.
          Elevation1 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX - 1, Camera.CellGridY - TileGrid.Offset + OffsetY - 1] / 12 * ElevationScale
          Elevation2 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 0, Camera.CellGridY - TileGrid.Offset + OffsetY - 1] / 12 * ElevationScale
          Elevation3 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 1, Camera.CellGridY - TileGrid.Offset + OffsetY - 1] / 12 * ElevationScale
          Elevation4 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX - 1, Camera.CellGridY - TileGrid.Offset + OffsetY + 0] / 12 * ElevationScale
          Elevation5 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 0, Camera.CellGridY - TileGrid.Offset + OffsetY + 0] / 12 * ElevationScale
          Elevation6 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 1, Camera.CellGridY - TileGrid.Offset + OffsetY + 0] / 12 * ElevationScale
          Elevation7 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX - 1, Camera.CellGridY - TileGrid.Offset + OffsetY + 1] / 12 * ElevationScale
          Elevation8 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 0, Camera.CellGridY - TileGrid.Offset + OffsetY + 1] / 12 * ElevationScale
          Elevation9 = dElevation[Camera.CellGridX - TileGrid.Offset + OffsetX + 1, Camera.CellGridY - TileGrid.Offset + OffsetY + 1] / 12 * ElevationScale
     
          ' Calculate tile's vertex coordinates.
          BottomLeft[0] = TileGrid.WorldX + OffsetX
          BottomLeft[1] = TileGrid.WorldY + OffsetY + 1
          BottomLeft[2] = (Elevation4 + Elevation7 + Elevation8 + Elevation5) / 4
          BottomRight[0] = TileGrid.WorldX + OffsetX + 1
          BottomRight[1] = TileGrid.WorldY + OffsetY + 1
          BottomRight[2] = (Elevation6 + Elevation8 + Elevation9 + Elevation5) / 4
          TopRight[0] = TileGrid.WorldX + OffsetX + 1
          TopRight[1] = TileGrid.WorldY + OffsetY
          TopRight[2] = (Elevation2 + Elevation3 + Elevation6 + Elevation5) / 4
          TopLeft[0] = TileGrid.WorldX + OffsetX
          TopLeft[1] = TileGrid.WorldY + OffsetY
          TopLeft[2] = (Elevation1 + Elevation2 + Elevation4 + Elevation5) / 4
     
          ' Select texture using its ID.
          Gl.BindTexture(Gl.TEXTURE_2D, tTileGrid[Convert.Wrap_Short(0, TileGrid.Size - 1, TileGridX), Convert.Wrap_Short(0, TileGrid.Size - 1, TileGridY)][0])
     
          ' Create the quad the texture is drawn on.
          Gl.Begin(Gl.QUADS)
            ' Calculate surface normal.
            Gl.Normal3fv(Convert.Normal(BottomLeft, BottomRight, TopRight)) ' No idea how to apply proper normal to the two triangles of the quad, per OP.
            ' Bottom-left vertex.
            Gl.TexCoord2i(0, 1)
            Gl.Vertex3f(BottomLeft[0], BottomLeft[1], BottomLeft[2])
            ' Bottom-right vertex.
            Gl.TexCoord2i(1, 1)
            Gl.Vertex3f(BottomRight[0], BottomRight[1], BottomRight[2])
            ' Top-right vertex.
            Gl.TexCoord2i(1, 0)
            Gl.Vertex3f(TopRight[0], TopRight[1], TopRight[2])
            ' Top-left vertex.
            Gl.TexCoord2i(0, 0)
            Gl.Vertex3f(TopLeft[0], TopLeft[1], TopLeft[2])
          Gl.End()
     
          ' Adjust tile position.
          OffsetX = OffsetX + 1
          If OffsetX = TileGrid.Size Then OffsetX = 0
     
        Next
     
        ' Adjust tile positions.
        OffsetX = 0
        OffsetY = OffsetY + 1
        If OffsetY = TileGrid.Size Then OffsetY = 0
     
      Next
     
    End

    To be clear, I barely know what I'm doing, thus the post in the Beginners forum. I just need to know how to shade a quad, side by side with other quads (as in smooth rather than faceted), or if it's possible. I'm not real clear on the logic (order of operations with respect to OpenGL calls) or even why normals aren't automatically calculated for a quad or other primitive. Shading's determined by defined light sources and calculated vectors perpendicular to each face/triangle; I get that. How to tell OpenGL to do its job in code is another matter entirely. I figured it'd be ultra simple to do this for a grid of quads, but it looks like the only simple thing is me.

  4. #4
    Super Moderator OpenGL Guru
    Join Date
    Feb 2000
    Location
    Montreal, Canada
    Posts
    4,256
    I noticed you called Gl.Enable(Gl.COLOR_MATERIAL). If you call that, then calls to glMaterial will have no effect.

    I noticed you called
    Gl.Materialf(Gl.FRONT, Gl.AMBIENT, 0)
    Gl.Materialf(Gl.FRONT, Gl.SPECULAR, 0)

    but I have no idea what that is suppose to be.
    I suggest that you use glMaterialfv and pass an RGBA value.

    In VB6, it looks like this
    DIM Ambient(4) as Single
    Ambient(0)=0.0
    Ambient(1)=0.0
    Ambient(2)=0.0
    Ambient(3)=0.0
    Call glMaterialfv(GL_FRONT, GL_AMBIENT, Ambient(0))


    To be clear, I barely know what I'm doing, thus the post in the Beginners forum. I just need to know how to shade a quad, side by side with other quads (as in smooth rather than faceted), or if it's possible. I'm not real clear on the logic (order of operations with respect to OpenGL calls) or even why normals aren't automatically calculated for a quad or other primitive. Shading's determined by defined light sources and calculated vectors perpendicular to each face/triangle; I get that. How to tell OpenGL to do its job in code is another matter entirely. I figured it'd be ultra simple to do this for a grid of quads, but it looks like the only simple thing is me.
    1. If you barely know what you are doing, then read the Red Book. http://www.glprogramming.com/red/
    2. It would look faceted probably because you are not smoothing the normal vectors between your quads.
    3. Your order of GL calls is fine.
    4. Normals are not calculated automatically by GL except in the case of glMap2d and glMap2f. These are for bezier surfaces.
    5. "I figured it'd be ultra simple to do this for a grid of quads, but it looks like the only simple thing is me". That depends on how motivated you are. If you want to solve the problem yourself. 3D graphics is probably the hardest part of the application programming branch.
    ------------------------------
    Sig: http://glhlib.sourceforge.net
    an open source GLU replacement library. Much more modern than GLU.
    float matrix[16], inverse_matrix[16];
    glhLoadIdentityf2(matrix);
    glhTranslatef2(matrix, 0.0, 0.0, 5.0);
    glhRotateAboutXf2(matrix, angleInRadians);
    glhScalef2(matrix, 1.0, 1.0, -1.0);
    glhQuickInvertMatrixf2(matrix, inverse_matrix);
    glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
    glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);

  5. #5
    Junior Member Newbie kevinfishburne's Avatar
    Join Date
    May 2012
    Location
    Stone Mountain, GA, USA
    Posts
    25
    Quote Originally Posted by V-man View Post
    Thanks for the tips on handling materials. Got those figure out now.

    Quote Originally Posted by V-man View Post
    1. If you barely know what you are doing, then read the Red Book. http://www.glprogramming.com/red/
    I'll be ordering a hard copy from Amazon, although the online version didn't have as much information as I thought in the sections I looked at.

    Quote Originally Posted by V-man View Post
    2. It would look faceted probably because you are not smoothing the normal vectors between your quads.
    What I've figured out so far is that I must calculate the normal of each triangle in the grid, then assign a normal to each triangle's vertex calculated from those values. To get blending I'll need to average the normals for triangle points which overlap. I think I also figured out how to normalize the magnitude of a normal (does OpenGL need them to be normalized?).

    Quote Originally Posted by V-man View Post
    5. "I figured it'd be ultra simple to do this for a grid of quads, but it looks like the only simple thing is me". That depends on how motivated you are. If you want to solve the problem yourself. 3D graphics is probably the hardest part of the application programming branch.
    Motivation isn't a problem. I've been making this game for over two years and it's everything to me. It's just maddening when my typical hacker mentality isn't enough (and it usually is). And OpenGL so far has been a bit ball busting, yes.

    This is my code for normal calculation (I'm using GAMBAS 3):

    Code :
    Public Function Normal(p1 As Single[], p2 As Single[], p3 As Single[]) As Single[]
     
      ' Calculate and return surface normal of specified triangle.
     
      ' General declarations.
      Dim N As New Single[3]
      Dim Magnitude As Single
     
      ' Calculate normal.
      N[0] = (p2[1] - p1[1]) * (p3[2] - p1[2]) - (p2[2] - p1[2]) * (p3[1] - p1[1])
      N[1] = (p2[2] - p1[2]) * (p3[0] - p1[0]) - (p2[0] - p1[0]) * (p3[2] - p1[2])
      N[2] = (p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0])
     
      ' Normalize normal.
      Magnitude = Sqr(N[0] ^ 2 + N[1] ^ 2 + N[2] ^ 2)
      N[0] = N[0] / Magnitude
      N[1] = N[1] / Magnitude
      N[2] = N[2] / Magnitude
     
      Return N
     
    End

    Hopefully that's correct. My other question would be what is a good reference for how a quad is drawn, exactly, based on the point specification order? I searched to no end on that one. Since I need to specify averaged normals for two triangles (six vertices) per quad, I don't quite understand how the triangles manifest based on the point order. Maybe I should just draw two triangles per tile and not use quads at all? Confused on that point obviously... I did at least graph out the ideal geometry for a height field on paper:

    http://eightvirtues.com/sanctimonia/...20Geometry.png

    Representing that with smooth shading is my ultimate goal. And thanks.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •