HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Rendering in the GL3 Viewport

Introduction to the GL3 Viewport

The GL3 Viewport uses modern GPU features, such as Vertex Buffer Objects (VBOs), shaders, FrameBuffer Objects (FBOs), Uniform Buffer Objects (UBOs) and special texture formats (integer textures, 2D texture arrays). It is based on OpenGL 3.3, and its primary goal is to shift more work away from the CPU and onto the GPU. Less work for the CPU means that geometry updates can occur faster, though at the expense of the viewport frame rate because the GPU is working harder. Generally this is a good tradeoff, as frame rates for tumbling for most models were above the 60fps mark at which the monitor can display them.

The result of this is that the GL3 viewport requires at least mid-range GPU hardware to run effectively, with a recommended minimum of 1GB of VRAM.

The GL3 Viewport also renders all geometry using shaders. Version 150 of the OpenGL shading language (GLSL) is used for Houdini's native primitives. However, this restriction doesn't extend to user GLSL shaders, providing they follow some basic rules:

  • No deprecated GL builtin uniform state is used. The GL3 viewport, for optimization purposes, does not initialize the lighting state, material, or GL transform matrices. Accessing these in a shader will return incorrect results.
  • No deprecated GL vertex shader attributes are used, such as gl_Vertex, gl_Normal, gl_MultiTexCoord0..8, and gl_Color, unless your GR_Primitive explicitly uses these vertex array bindings. All Houdini primitives use generic vertex attributes, with the same names as their Houdini attribute counterparts (P, N, Cd, Alpha, uv).
  • Either different shaders are used for different render modes, such as the beauty pass (forward-style renderer) and material pass (deferred shading renderer), or fragment shader outputs are selectively written to the correct locations.

Vertex Shader Inputs

Unlike GL2, GL3 vertex shaders use inputs that are named the same as their Houdini counterparts. For example:

#version 150
in vec3 P;
in vec3 N;
in vec3 Cd;
in vec3 Alpha;
in vec3 uv;

These inputs are all point or detail attributes (varying or constant). Access to primitive or vertex attributes must be done in a geometry shader:

#version 150
in vec3 color[]; // from vertex shader, point or detail
uniform samplerBuffer attrmodeCd;
uniform samplerBuffer attrCd;
int HOUprimitiveID(out ivec3 vertex);
void main()
{
int prim_id;
ivec3 vertex_id;
vec3 Cd;
prim_id = HOUprimitive(vertex);
if(attrmodeCd == 1) // PRIMITIVE
Cd = texelFetch(attrCd, prim_id).rgb;
else if(attrmodeCd == 2) // VERTEX
Cd = texelFetch(attrCd, vertex.x).rgb // first vertex; 2=y,3=z
else // POINT or DETAIL
Cd = color[0];
}

Any Houdini attribute type can be sampled this way, as long as it has between 1 and 4 components. Attributes with greater than 4 components need to be split into multiple GL attributes.

String attributes are not supported because GLSL does not support strings; partitioning of the geometry based on the string table is one way to get around this restriction. Each partition can them be setup uniquely and drawn (this is how shop_material_path is handled).

OpenGL has minimum limits for both the number of vertex shader inputs and the number of textures that can be accessed in a shader stage. For GL3.3, this is 16 vertex inputs and 16 textures, though specific implementations may have higher limits. Matrix vertex inputs take up more than one attribute location (2 for mat2, 3 for mat3, 4 for mat4), and double precision inputs may take up more than one location. It is possible to get around the vertex input limit by combining two or more Houdini attributes in a single GL attribute (say, Cd+Alpha, or uv+uv2), or by using a Texture Buffer Object to sample an attribute using texelFetch().

Because GL3 uses generic vertex inputs, rendering a GL3 object without a shader will not render anything. While shaders may seem like a bit of a burden initially, they are much easier to debug and maintain because the amount of GL state used by shaders is substantially reduced. While the fixed function state can be difficult to determine, a shader's source is is easy to view.

GL3 Rendering Environment

The GL3 rendering environment is made up of uniforms and uniform blocks. Uniforms are used for most of the global state, while uniform blocks tend to represent actual objects (lights, materials). Uniform blocks are buffer objects that can be easily seperated from the shader state, which is handy for keeping them with the C++ object that they represent.

Lighting Blocks

The lighting state is set up in uniform blocks for lights 0 to 10.

layout(std140) uniform glH_Light0
{
vec3 pos; // light position, world space
vec3 dir; // light direction, world space
vec3 atten; // attenuation - .x: d^2, .y: d, .z: const
vec3 amb; // ambient color component
vec3 spec; // specular component
vec3 diff; // diffuse component
float active_radius; // max light distance, won't contribute past this distance
float coscutoff; // cosine(cutoff) to compare to dot(dir,P-pos)
float cosfalloff; // cosine(cutoff+falloff) to compare to dot(dir,P-pos)
bool point; // point light (true), spot light (false)
} lightSource0;

These uniform blocks can be set up using RE_LightList::bindForShader(). The light list can be queried from GR_CommonDispOptions::getLightList().

You can also use the Houdini builtin GLSL functions, such as HOUlightingModel(), to handle lighting for you.

void HOUlightingModel(in vec3 P,
in vec3 nN,
in vec3 mAmb,
in vec3 mDiff,
in vec3 mSpec,
in vec3 mMetal,
inout vec3 lAmb,
inout vec3 lDiff,
inout vec3 lSpec,
in float rough,
in float reflect_co,
in float metallic,
in int spec_model,
in float alpha);

The function will automatically link into your shader if its name is found in the shader and it is loaded via RE_Shader. Otherwise, it can be found in glsl/houlib/GL32/lighting_model.func.

High Quality lighting uses a deferred shading scheme, so lighting should not be computed in this case. HQ Lighting is active if glH_MaterialPass is 1.

Material Block

The material block can be set up using RE_Material::updateShaderForMaterial(). This will populate the material uniform block with values form the current material. A material also has texture samplers which cannot reside inside a uniform block, so additional sampler uniforms are required.

layout(std140) uniform glH_Material
{
vec3 ambient_color; // material color components
vec3 diffuse_color;
vec3 emission_color;
vec3 specular_color;
float material_alpha; // perpendicular alpha
float material_alpha_para; // parallel alpha
float shininess; // specular shininess
bool has_diffuse_map; // diffuse texture present
bool has_opacity_map; // opacity texture present
bool has_emission_map; // emission texture present
bool has_bump_map; // bump texture present
int bumpComps; // bump map # components:
// 1 - take gradient of map
// 2 - uv bump map
// 3 - xyz normal map
vec2 bumpMapSize; // bump map resolution
bool bumpBias; // true [0,1], false [-1,1]
bool bumpInvert; // invert bump map (1-bump)
float bumpScale; // scale bumps by a factor
bool has_spec_map; // specular map present
bool specularShinyAdjust; // map adjusts shininess with A
vec2 shinyRange; // map A to shinyRange.x - .y
bool has_env_map; // environment map present
float envScale; // scale Cenv by factor
mat3 envRotate; // rotate matrix for map
};
uniform sampler2D glH_DiffuseMap; // diffuse texture map
uniform sampler2D glH_OpacityMap; // opacity texture map
uniform sampler2D glH_EmissionMap; // emission texture map
uniform sampler2D glH_BumpMap; // bump or normal map
uniform sampler2D glH_SpecularMap; // specular map
uniform samplerCube glH_EnvMap; // environment reflection map

Every material has four color components that can be applied during lighting (multiplied by the shader-computed lighting components) and two alpha components. The default alpha is for eye rays perpendicular to the surface; alpha_para is for eye rays parallel to its silhouette edges. Most of the time these will be the same, but for simulating the fresnel of glass, they can be different.

The next three sections deal with lighting maps - bump, normal, specular and reflection (environment) maps.

Bump maps create the illusion of an uneven surface without modifying the geometry itself by perturbing normals on a per-pixel basis. The bump map does double duty supporting bump and normal maps. If has_bump_maps is true, a bump or normal map exists. The bumpComps determines the type of map being used. A 1 component map is considered to be a height map, and the gradients of the map are determined to perturb the normal. A 2 component map is considered to be a regular uv bump map. A 3 component map is considered to be a normal map, which replaces the normals entirely. The bumpBias specifies whether the map's range is [0,1], which would be a regular 8b map, or [-1,1] for a floating point format. bumpInvert will invert the direction of the bumps, while bumpScale will exagerate (>1) or diminish (<1) the bumps.

Specular maps allow areas to be designated as shiny and others dull. The RGB components of a specular map define the specular reflection color, while the alpha can adjust the shininess of the surface (if specularShinyAdjust is true). The shinyRange parameter maps the [0,1] alpha channel to the extents of the shininess (min 0, max 128).

The environment map allows the surface to appear to reflect its surroundings (the contents of the map). Environment maps are cubemaps. The reflection intensity can be adjusted with envScale, and the entire map rotated around the object using envRotate.

Global Shader State

In addition to the lighting and material blocks, there are also builtin Houdini uniforms which can be accessed from any shader stage:

  • glH_Emission (float) - 1.0 if emission components are rendering, 0.0 otherwise. The reason this is a float is so that it can multiply the emission component instead of branching, as computation is almost always faster than branching on a GPU.
  • glH_Specular (float) - 1.0 if specular components are rendering, 0.0 otherwise
  • glH_Diffuse (float) - 1.0 if diffuse components are rendering, 0.0 otherwise
  • glH_Ambient (float) - 1.0 if ambient components are rendering, 0.0 otherwise
  • glH_MaterialPass (int) - 0 for normal forward rendering, 1 for deferred rendering (GR_RENDER_MATERIAL)
  • glH_AlphaPass (int) - 0 for the opaque pass, 1 for transparency pass(es)
  • glH_GhostColor (vec4) - ghost color, RGBA.
  • glH_SelectionColor (vec4) - selection color, RGBA.
  • glH_SelectMode (int) - selection mode:
    • 0: no selection
    • 1: partial primitive selection
    • 2: full primitive selection
    • 3: partial point selection
    • 4: full point selection
    • 5: partial vertex selection
    • 6: full vertex selection
  • glH_FillSelection (int) - selection hue is displayed on selected polygons in wire-over-shaded (1), polygon shading left as-is (0), or all polygons shaded with selection color (2)
  • glH_LightingEnabled (int) - Lighting is enabled
  • glH_LightMask (int) - bitmask of lights that are enabled for the current object
  • glH_ViewMatrix (mat4) - transform matrix for the current viewport frustum (world->eye)
  • glH_InvViewMatrix (mat4) - inverse transform matrix for the current viewport frusum (eye->world)
  • glH_ObjectMatrix (mat4) - transform matrix for the current object (local->world)
  • glH_InvObjectMatrix (mat4) - inverse transform matrix for the current object (world->local)
  • glH_NormalMatrix (mat3) - view transform for rotating normals
  • glH_ProjectMatrix (mat4) - projection matrix from 3D eye space to screen space (eye->NDC)
  • glH_InvProjectMatrix (mat4) - inverse projection matrix from screen space to 3D space (NDC->eye)
  • glH_ScreenSize (vec2) - dimension of the current viewport, in pixels
  • glH_NumSamples (int) - number of samples used when multisampling
  • glH_IsOrtho (int) - 0 if perspective, 1 if orthographic view
  • glH_DepthRange (vec2) - near/far plane values
  • glH_DepthOffset (vec2) - polygon offset values. Variable offset in .x (multiplied by dZ), constant offset in .y
  • glH_WireOver (int) - 1 if wire-over-shaded is active, 0 otherwise.
  • glH_WireColor (vec4) - color of the default wireframe, RGBA
  • glH_WireThickness (float) - thickness of the current wireframe
  • glH_ConstColor (vec4) - constant color used for mattes, hidden line backgrounds, RGBA
  • glH_PrimConvexed (int) - 1 if the primitive has been convexed, 0 otherwise.
  • glH_PickID (ivec4) - pick ID for this object.
  • glH_ColorRamp (sampler1D) - 1D texture sampler lookup table for color ramps.
  • glH_ColorRampEnable (int) - 1 if color ramp LUT is active, 0 otherwise.
  • glH_ColorRange (vec2) - remap color into a new range - C * glH_ColorRange.x + glH_ColorRange.y.