第8章 Cg函数编程与标准库应用

第8章 Cg函数编程与标准库应用

在图形编程中,函数是构建复杂着色器的基础模块。本章将深入探讨Cg语言中的函数特性、程序结构设计以及强大的标准函数库。通过学习函数参数传递、重载机制、入口函数定义以及各类实用函数库,读者将能够编写出更加模块化、高效且功能丰富的着色器程序。我们将结合Cg着色语言与C++/OpenGL应用程序,提供完整的可运行实例,帮助读者在VSCODE和VS2022环境中深入理解这些概念。

8.1 函数基础与参数传递

函数在Cg中扮演着代码复用的关键角色,它们允许将复杂操作封装为独立的单元。与C语言类似,Cg函数包含返回类型、函数名、参数列表和函数体。然而,由于图形硬件的特殊性,Cg函数在设计时需要考虑性能优化和硬件限制。

理论概念:

函数声明与定义:Cg支持函数原型声明和具体实现参数传递方式:值传递、引用传递(通过inout关键字)返回值类型:可以是基本类型(float、int等)或结构体函数作用域:遵循类似C语言的块作用域规则

下面是一个简单的函数示例,演示如何在Cg中定义和使用函数,以及如何在C++应用程序中集成这些着色器。

Cg着色器代码(保存为
function_basic.cg
):


// 基础函数示例:计算颜色的亮度
float calculateLuminance(float3 color)
{
    // 使用标准亮度公式:0.299*R + 0.587*G + 0.114*B
    return dot(color, float3(0.299, 0.587, 0.114));
}

// 修改颜色亮度的函数
float3 adjustBrightness(float3 originalColor, float brightnessFactor)
{
    float luminance = calculateLuminance(originalColor);
    float3 adjustedColor = originalColor * brightnessFactor;
    
    // 确保颜色值在有效范围内
    adjustedColor = clamp(adjustedColor, 0.0, 1.0);
    return adjustedColor;
}

// 顶点着色器
struct VertexInput {
    float4 position : POSITION;
    float4 color : COLOR0;
};

struct VertexOutput {
    float4 position : POSITION;
    float4 color : COLOR0;
};

VertexOutput main(VertexInput input)
{
    VertexOutput output;
    output.position = input.position;
    
    // 使用自定义函数调整颜色亮度
    float3 originalRGB = input.color.rgb;
    float3 brightenedColor = adjustBrightness(originalRGB, 1.5);
    
    output.color = float4(brightenedColor, input.color.a);
    return output;
}

C++应用程序代码:


#include <GL/glew.h>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#include <iostream>
#include <vector>

CGcontext context;
CGprogram vertexProgram;

void initCg() {
    context = cgCreateContext();
    
    // 检查Cg配置文件是否可用
    CGprofile profile = CG_PROFILE_VP20;
    if (cgGLIsProfileSupported(profile)) {
        std::cout << "Profile VP20 supported" << std::endl;
    } else {
        std::cout << "Profile VP20 not supported, trying ARBVP1" << std::endl;
        profile = CG_PROFILE_ARBVP1;
    }
    
    vertexProgram = cgCreateProgramFromFile(context, CG_SOURCE, 
                                           "function_basic.cg", profile, NULL, NULL);
    if (!vertexProgram) {
        std::cerr << "Failed to create Cg program: " << cgGetErrorString(cgGetError()) << std::endl;
        return;
    }
    
    cgGLLoadProgram(vertexProgram);
    std::cout << "Cg program loaded successfully" << std::endl;
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    cgGLBindProgram(vertexProgram);
    cgGLEnableProfile(CG_PROFILE_VP20);
    
    // 绘制彩色三角形
    glBegin(GL_TRIANGLES);
    glColor3f(0.6f, 0.2f, 0.2f);  // 暗红色
    glVertex3f(-0.5f, -0.5f, 0.0f);
    
    glColor3f(0.2f, 0.6f, 0.2f);  // 暗绿色
    glVertex3f(0.5f, -0.5f, 0.0f);
    
    glColor3f(0.2f, 0.2f, 0.6f);  // 暗蓝色
    glVertex3f(0.0f, 0.5f, 0.0f);
    glEnd();
    
    cgGLDisableProfile(CG_PROFILE_VP20);
    glutSwapBuffers();
}

void reshape(int width, int height) {
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (double)width/height, 0.1, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Cg函数基础示例");
    
    glewInit();
    initCg();
    
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    
    std::cout << "使用Cg函数调整颜色亮度 - 三角形应该比原始颜色更亮" << std::endl;
    
    glutMainLoop();
    return 0;
}

编译与运行说明:
在VS2022中创建控制台应用程序,配置OpenGL、GLUT和Cg库。在VSCODE中,可以使用类似的CMake配置。这个示例展示了如何定义和使用自定义函数来计算和调整颜色亮度,通过Cg着色器实现实时的颜色处理效果。

8.1.1 数组参数处理

数组作为函数参数在Cg中具有特殊的重要性,特别是在处理顶点数据、颜色数组或纹理采样时。Cg支持一维数组作为函数参数,但需要注意数组大小在编译时通常是固定的。

理论概念:

数组参数声明:可以在函数参数中使用固定大小的数组数组传递:默认情况下是值传递,但大型数组应考虑性能影响多维数组:Cg支持多维数组作为参数,常用于矩阵操作动态大小限制:与C不同,Cg数组大小通常在编译时确定

下面是一个使用数组参数的进阶示例,演示如何通过数组实现简单的颜色渐变效果。

Cg着色器代码(保存为
array_parameter.cg
):


// 使用数组参数的函数示例
float3 applyColorGradient(float3 baseColor, float gradientWeights[3])
{
    float3 resultColor;
    
    // 使用数组权重混合颜色分量
    resultColor.r = baseColor.r * gradientWeights[0];
    resultColor.g = baseColor.g * gradientWeights[1]; 
    resultColor.b = baseColor.b * gradientWeights[2];
    
    return clamp(resultColor, 0.0, 1.0);
}

// 处理颜色数组的函数
float3 blendMultipleColors(float3 colorArray[4], float blendFactors[4])
{
    float3 finalColor = float3(0.0, 0.0, 0.0);
    
    // 加权混合多个颜色
    for (int i = 0; i < 4; i++) {
        finalColor += colorArray[i] * blendFactors[i];
    }
    
    return clamp(finalColor, 0.0, 1.0);
}

// 顶点着色器主函数
struct VertexInput {
    float4 position : POSITION;
    float4 color : COLOR0;
};

struct VertexOutput {
    float4 position : POSITION;
    float4 color : COLOR0;
};

VertexOutput main(VertexInput input)
{
    VertexOutput output;
    output.position = input.position;
    
    // 定义渐变权重数组
    float gradientWeights[3];
    gradientWeights[0] = 1.2;  // 增强红色
    gradientWeights[1] = 0.8;  // 减弱绿色  
    gradientWeights[2] = 1.5;  // 增强蓝色
    
    // 应用颜色渐变
    float3 processedColor = applyColorGradient(input.color.rgb, gradientWeights);
    
    // 多颜色混合示例
    float3 colorPalette[4];
    colorPalette[0] = float3(1.0, 0.0, 0.0); // 红
    colorPalette[1] = float3(0.0, 1.0, 0.0); // 绿
    colorPalette[2] = float3(0.0, 0.0, 1.0); // 蓝
    colorPalette[3] = float3(1.0, 1.0, 0.0); // 黄
    
    float blendFactors[4];
    blendFactors[0] = 0.3;
    blendFactors[1] = 0.3; 
    blendFactors[2] = 0.3;
    blendFactors[3] = 0.1;
    
    float3 blendedColor = blendMultipleColors(colorPalette, blendFactors);
    
    // 结合两种效果
    float3 finalColor = (processedColor + blendedColor) * 0.5;
    
    output.color = float4(finalColor, input.color.a);
    return output;
}

C++应用程序代码:


#include <GL/glew.h>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#include <iostream>

CGcontext context;
CGprogram vertexProgram;

void initCg() {
    context = cgCreateContext();
    CGprofile profile = CG_PROFILE_VP20;
    
    vertexProgram = cgCreateProgramFromFile(context, CG_SOURCE, 
                                           "array_parameter.cg", profile, NULL, NULL);
    if (vertexProgram) {
        cgGLLoadProgram(vertexProgram);
        std::cout << "数组参数示例程序加载成功" << std::endl;
    } else {
        std::cerr << "无法创建Cg程序: " << cgGetErrorString(cgGetError()) << std::endl;
    }
}

void display() {
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    cgGLBindProgram(vertexProgram);
    cgGLEnableProfile(CG_PROFILE_VP20);
    
    // 绘制多个三角形展示颜色效果
    glBegin(GL_TRIANGLES);
    
    // 第一个三角形 - 红色调
    glColor3f(0.8f, 0.3f, 0.3f);
    glVertex3f(-0.6f, -0.5f, 0.0f);
    glColor3f(0.6f, 0.2f, 0.2f);
    glVertex3f(-0.1f, -0.5f, 0.0f);
    glColor3f(0.9f, 0.4f, 0.4f);
    glVertex3f(-0.35f, 0.3f, 0.0f);
    
    // 第二个三角形 - 绿色调
    glColor3f(0.3f, 0.8f, 0.3f);
    glVertex3f(0.1f, -0.5f, 0.0f);
    glColor3f(0.2f, 0.6f, 0.2f);
    glVertex3f(0.6f, -0.5f, 0.0f);
    glColor3f(0.4f, 0.9f, 0.4f);
    glVertex3f(0.35f, 0.3f, 0.0f);
    
    glEnd();
    
    cgGLDisableProfile(CG_PROFILE_VP20);
    glutSwapBuffers();
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Cg数组参数示例");
    
    glewInit();
    initCg();
    
    glutDisplayFunc(display);
    
    std::cout << "演示Cg函数中的数组参数使用 - 观察颜色渐变和混合效果" << std::endl;
    
    glutMainLoop();
    return 0;
}

实例说明:
这个示例展示了如何在Cg函数中使用数组参数来实现复杂的颜色处理。第一个函数
applyColorGradient
使用权重数组调整各个颜色通道,第二个函数
blendMultipleColors
演示了如何混合多个颜色源。在C++应用程序中,我们绘制了两个三角形来展示这些颜色效果。

8.2 函数重载机制

函数重载是Cg语言中的重要特性,它允许在同一作用域内定义多个同名函数,只要它们的参数列表不同。这种机制提高了代码的可读性和灵活性,特别在图形编程中处理不同类型的数据时非常有用。

理论概念:

重载解析:编译器根据参数数量和类型选择最匹配的函数返回值类型:仅返回值不同不能构成重载应用场景:处理不同精度、不同维度的数据性能考虑:重载不会带来运行时开销,在编译时解析

下面通过一个详细的示例展示Cg中的函数重载,实现针对不同数据类型的向量操作。

Cg着色器代码(保存为
function_overload.cg
):


// 函数重载示例:不同精度的距离计算

// 浮点数版本的距离计算
float calculateDistance(float2 pointA, float2 pointB)
{
    float2 delta = pointA - pointB;
    return sqrt(delta.x * delta.x + delta.y * delta.y);
}

// 半精度浮点数版本
half calculateDistance(half2 pointA, half2 pointB)
{
    half2 delta = pointA - pointB;
    return sqrt(delta.x * delta.x + delta.y * delta.y);
}

// 三维点距离计算
float calculateDistance(float3 pointA, float3 pointB)
{
    float3 delta = pointA - pointB;
    return sqrt(dot(delta, delta));
}

// 带权重的距离计算(参数数量不同构成重载)
float calculateDistance(float2 pointA, float2 pointB, float2 weights)
{
    float2 delta = pointA - pointB;
    delta *= weights; // 应用权重
    return sqrt(dot(delta, delta));
}

// 颜色混合函数的重载示例

// 基础颜色混合
float3 blendColors(float3 colorA, float3 colorB, float factor)
{
    return lerp(colorA, colorB, factor);
}

// 带alpha通道的颜色混合
float4 blendColors(float4 colorA, float4 colorB, float factor)
{
    return lerp(colorA, colorB, factor);
}

// 多颜色混合(参数数量不同)
float3 blendColors(float3 colorA, float3 colorB, float3 colorC, float3 factors)
{
    return colorA * factors.x + colorB * factors.y + colorC * factors.z;
}

// 顶点着色器主程序
struct VertexInput {
    float4 position : POSITION;
    float4 color : COLOR0;
    float2 texcoord : TEXCOORD0;
};

struct VertexOutput {
    float4 position : POSITION;
    float4 color : COLOR0;
    float2 texcoord : TEXCOORD0;
};

VertexOutput main(VertexInput input)
{
    VertexOutput output;
    output.position = input.position;
    output.texcoord = input.texcoord;
    
    // 演示不同重载函数的使用
    float2 point1 = float2(0.0, 0.0);
    float2 point2 = float2(input.texcoord.x, input.texcoord.y);
    
    // 调用二维点距离计算
    float dist2D = calculateDistance(point1, point2);
    
    // 调用带权重的距离计算
    float2 weights = float2(1.0, 2.0); // Y方向权重更高
    float weightedDist = calculateDistance(point1, point2, weights);
    
    // 使用距离值影响颜色
    float distanceFactor = clamp(dist2D * 2.0, 0.0, 1.0);
    
    // 调用颜色混合函数
    float3 baseColor = input.color.rgb;
    float3 targetColor = float3(1.0, 0.5, 0.0); // 橙色
    
    // 使用不同的混合函数
    float3 blendedColor = blendColors(baseColor, targetColor, distanceFactor);
    
    // 使用多颜色混合
    float3 additionalColor = float3(0.2, 0.8, 0.2); // 绿色
    float3 blendFactors = float3(0.6, 0.3, 0.1);
    float3 multiBlended = blendColors(baseColor, targetColor, additionalColor, blendFactors);
    
    // 结合两种混合结果
    float3 finalColor = (blendedColor + multiBlended) * 0.5;
    
    output.color = float4(finalColor, input.color.a);
    return output;
}

C++应用程序代码:


#include <GL/glew.h>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#include <iostream>
#include <cmath>

CGcontext context;
CGprogram vertexProgram;

void initCg() {
    context = cgCreateContext();
    CGprofile profile = CG_PROFILE_VP20;
    
    vertexProgram = cgCreateProgramFromFile(context, CG_SOURCE, 
                                           "function_overload.cg", profile, NULL, NULL);
    if (vertexProgram) {
        cgGLLoadProgram(vertexProgram);
        std::cout << "函数重载示例程序加载成功" << std::endl;
    } else {
        std::cerr << "无法创建Cg程序: " << cgGetErrorString(cgGetError()) << std::endl;
    }
}

void display() {
    glClearColor(0.2f, 0.2f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    cgGLBindProgram(vertexProgram);
    cgGLEnableProfile(CG_PROFILE_VP20);
    
    // 绘制四边形展示纹理坐标效果
    glBegin(GL_QUADS);
    
    // 左下角
    glColor3f(0.8f, 0.2f, 0.2f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-0.8f, -0.8f, 0.0f);
    
    // 右下角  
    glColor3f(0.2f, 0.8f, 0.2f);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(0.8f, -0.8f, 0.0f);
    
    // 右上角
    glColor3f(0.2f, 0.2f, 0.8f);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(0.8f, 0.8f, 0.0f);
    
    // 左上角
    glColor3f(0.8f, 0.8f, 0.2f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(-0.8f, 0.8f, 0.0f);
    
    glEnd();
    
    cgGLDisableProfile(CG_PROFILE_VP20);
    glutSwapBuffers();
}

void reshape(int width, int height) {
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float aspect = (float)width / height;
    if (width >= height) {
        glOrtho(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0, -1.0, 1.0);
    } else {
        glOrtho(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect, -1.0, 1.0);
    }
    glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Cg函数重载机制示例");
    
    glewInit();
    initCg();
    
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    
    std::cout << "演示Cg函数重载 - 观察基于纹理坐标的颜色渐变效果" << std::endl;
    std::cout << "距离中心越远,颜色混合效果越明显" << std::endl;
    
    glutMainLoop();
    return 0;
}

实例分析:
这个示例全面展示了Cg中的函数重载机制。我们定义了多个版本的
calculateDistance

blendColors
函数,它们根据参数类型和数量的不同而被区分。在顶点着色器中,我们使用纹理坐标来计算距离,并基于这些距离值调用不同的重载函数来创建颜色效果。这种方法使得代码更加清晰且易于维护。

8.3 着色器入口函数

入口函数是Cg着色器的执行起点,类似于C语言中的main函数。理解入口函数的特性和规范对于编写正确的着色器至关重要。在Cg中,顶点着色器和片段着色器都有各自的入口函数,它们负责处理特定类型的图形数据。

理论概念:

函数命名:传统上使用
main
作为入口函数名参数类型:入口函数通常接受结构体参数,包含输入语义返回值:返回包含输出语义的结构体多入口点:复杂着色器可能包含多个入口函数

下面通过一个综合示例展示入口函数的各种用法和最佳实践。

Cg着色器代码(保存为
entry_function.cg
):


// 入口函数综合示例

// 自定义数据结构
struct AppData {
    float4 position : POSITION;
    float4 color : COLOR0;
    float3 normal : NORMAL;
    float2 texcoord : TEXCOORD0;
};

struct VertexOutput {
    float4 position : POSITION;
    float4 color : COLOR0;
    float3 worldNormal : TEXCOORD1;
    float2 uv : TEXCOORD0;
};

// 辅助函数:计算简单光照
float3 computeSimpleLighting(float3 normal, float3 lightDir)
{
    float intensity = max(dot(normal, lightDir), 0.0);
    return float3(intensity, intensity, intensity);
}

// 辅助函数:处理纹理坐标
float2 transformTexcoord(float2 originalUV)
{
    // 简单的纹理坐标变换:旋转和缩放
    float2 center = float2(0.5, 0.5);
    float2 offset = originalUV - center;
    
    // 简单旋转
    float angle = 0.5; // 旋转角度
    float2x2 rotMatrix = float2x2(
        cos(angle), -sin(angle),
        sin(angle), cos(angle)
    );
    
    float2 rotated = mul(rotMatrix, offset);
    return rotated + center;
}

// 主要的顶点着色器入口函数
VertexOutput main(AppData input)
{
    VertexOutput output;
    
    // 处理位置坐标
    output.position = input.position;
    
    // 处理法线向量(简单转换为世界空间)
    output.worldNormal = normalize(input.normal);
    
    // 变换纹理坐标
    output.uv = transformTexcoord(input.texcoord);
    
    // 计算简单光照
    float3 lightDirection = normalize(float3(0.5, 1.0, 0.5));
    float3 lighting = computeSimpleLighting(output.worldNormal, lightDirection);
    
    // 应用光照到颜色
    output.color = input.color * float4(lighting, 1.0);
    
    return output;
}

// 片段着色器入口函数示例
struct FragmentInput {
    float4 color : COLOR0;
    float3 worldNormal : TEXCOORD1;
    float2 uv : TEXCOORD0;
};

struct FragmentOutput {
    float4 color : COLOR;
};

// 片段着色器入口函数
FragmentOutput fragmentMain(FragmentInput input)
{
    FragmentOutput output;
    
    // 基于UV坐标创建简单图案
    float2 centeredUV = input.uv - float2(0.5, 0.5);
    float radial = length(centeredUV);
    float circle = 1.0 - smoothstep(0.3, 0.4, radial);
    
    // 基于法线创建简单效果
    float normalEffect = (input.worldNormal.y + 1.0) * 0.5;
    
    // 组合各种效果
    float3 finalColor = input.color.rgb;
    finalColor = lerp(finalColor, float3(1.0, 1.0, 1.0), circle * 0.3);
    finalColor *= (0.7 + 0.3 * normalEffect);
    
    output.color = float4(finalColor, input.color.a);
    return output;
}

C++应用程序代码:


#include <GL/glew.h>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#include <iostream>
#include <vector>

CGcontext context;
CGprogram vertexProgram, fragmentProgram;

void initCg() {
    context = cgCreateContext();
    
    // 初始化顶点着色器
    vertexProgram = cgCreateProgramFromFile(context, CG_SOURCE, 
                                           "entry_function.cg", CG_PROFILE_VP20, 
                                           "main", NULL);
    if (vertexProgram) {
        cgGLLoadProgram(vertexProgram);
        std::cout << "顶点着色器入口函数程序加载成功" << std::endl;
    }
    
    // 初始化片段着色器  
    fragmentProgram = cgCreateProgramFromFile(context, CG_SOURCE,
                                             "entry_function.cg", CG_PROFILE_FP20,
                                             "fragmentMain", NULL);
    if (fragmentProgram) {
        cgGLLoadProgram(fragmentProgram);
        std::cout << "片段着色器入口函数程序加载成功" << std::endl;
    }
}

// 创建简单的几何体数据
void createGeometry(std::vector<float>& vertices, std::vector<float>& normals, 
                   std::vector<float>& texcoords) {
    // 创建一个金字塔形状(4个三角形)
    float pyramidVertices[] = {
        // 底面
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        // 顶点
         0.0f,  0.5f,  0.0f
    };
    
    float pyramidNormals[] = {
        // 底面法线(向下)
        0.0f, -1.0f, 0.0f,
        0.0f, -1.0f, 0.0f, 
        0.0f, -1.0f, 0.0f,
        0.0f, -1.0f, 0.0f,
        // 侧面法线(在绘制时计算)
        0.0f, 0.0f, 1.0f
    };
    
    float pyramidTexcoords[] = {
        0.0f, 0.0f,
        1.0f, 0.0f, 
        1.0f, 1.0f,
        0.0f, 1.0f,
        0.5f, 0.5f
    };
    
    vertices.assign(pyramidVertices, pyramidVertices + 15);
    normals.assign(pyramidNormals, pyramidNormals + 15);
    texcoords.assign(pyramidTexcoords, pyramidTexcoords + 10);
}

void display() {
    glClearColor(0.1f, 0.15f, 0.2f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    
    // 绑定着色器程序
    cgGLBindProgram(vertexProgram);
    cgGLEnableProfile(CG_PROFILE_VP20);
    cgGLBindProgram(fragmentProgram);
    cgGLEnableProfile(CG_PROFILE_FP20);
    
    // 旋转动画
    static float angle = 0.0f;
    angle += 0.5f;
    glLoadIdentity();
    gluLookAt(0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    glRotatef(angle, 0.0f, 1.0f, 0.0f);
    
    // 绘制金字塔
    glBegin(GL_TRIANGLES);
    
    // 底面(两个三角形)
    glColor3f(0.8f, 0.3f, 0.3f);
    glNormal3f(0.0f, -1.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.5f, -0.5f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, -0.5f, 0.5f);
    
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, -0.5f, 0.5f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, -0.5f, 0.5f);
    
    // 四个侧面
    glColor3f(0.3f, 0.8f, 0.3f);
    // 前面
    glNormal3f(0.0f, 0.4f, 0.9f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.5f, 0.5f);
    glTexCoord2f(0.5f, 1.0f); glVertex3f(0.0f, 0.5f, 0.0f);
    
    glColor3f(0.3f, 0.3f, 0.8f);
    // 右面
    glNormal3f(0.9f, 0.4f, 0.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f, -0.5f, 0.5f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.5f, -0.5f);
    glTexCoord2f(0.5f, 1.0f); glVertex3f(0.0f, 0.5f, 0.0f);
    
    glColor3f(0.8f, 0.8f, 0.3f);
    // 后面
    glNormal3f(0.0f, 0.4f, -0.9f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f, -0.5f, -0.5f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f);
    glTexCoord2f(0.5f, 1.0f); glVertex3f(0.0f, 0.5f, 0.0f);
    
    glColor3f(0.8f, 0.3f, 0.8f);
    // 左面
    glNormal3f(-0.9f, 0.4f, 0.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f);
    glTexCoord2f(0.5f, 1.0f); glVertex3f(0.0f, 0.5f, 0.0f);
    
    glEnd();
    
    cgGLDisableProfile(CG_PROFILE_VP20);
    cgGLDisableProfile(CG_PROFILE_FP20);
    glutSwapBuffers();
}

void idle() {
    glutPostRedisplay();
}

void reshape(int width, int height) {
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (double)width/height, 0.1, 100.0);
    glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Cg入口函数示例 - 旋转金字塔");
    
    glewInit();
    initCg();
    
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutIdleFunc(idle);
    
    std::cout << "演示Cg入口函数使用 - 观察旋转金字塔的光照和纹理效果" << std::endl;
    
    glutMainLoop();
    return 0;
}

实例分析:
这个示例展示了Cg入口函数的完整用法。我们定义了顶点着色器入口函数
main
和片段着色器入口函数
fragmentMain
,它们分别处理不同类型的图形数据。示例中还演示了如何:

使用结构体组织输入输出数据在入口函数中调用辅助函数处理法线向量和纹理坐标创建简单的光照效果

C++应用程序创建了一个旋转的金字塔模型,展示了着色器在实际3D场景中的应用。

8.4 Cg标准函数库应用

Cg标准函数库提供了一系列优化的内置函数,涵盖了数学运算、几何计算、纹理操作等常见图形编程任务。这些函数经过硬件优化,能够提供比手动实现更好的性能。

8.4.1 数学函数应用

数学函数是图形编程的基础,Cg提供了丰富的数学函数库,包括三角函数、指数函数、取整函数等。

理论概念:

三角函数:sin, cos, tan, asin, acos, atan等指数对数函数:exp, log, pow, sqrt等取整函数:floor, ceil, round, frac等插值函数:lerp, smoothstep等

下面通过一个综合示例展示数学函数的实际应用。

Cg着色器代码(保存为
math_functions.cg
):


// 数学函数应用示例

struct VertexInput {
    float4 position : POSITION;
    float4 color : COLOR0;
    float2 texcoord : TEXCOORD0;
};

struct VertexOutput {
    float4 position : POSITION;
    float4 color : COLOR0;
    float2 texcoord : TEXCOORD0;
};

// 使用数学函数创建波浪效果
float3 applyWaveEffect(float3 originalPos, float time)
{
    float3 newPos = originalPos;
    
    // 使用sin函数创建波浪
    float wave1 = sin(originalPos.x * 5.0 + time) * 0.1;
    float wave2 = cos(originalPos.z * 3.0 + time * 1.5) * 0.05;
    
    newPos.y += wave1 + wave2;
    return newPos;
}

// 使用数学函数创建颜色模式
float3 createMathPattern(float2 uv, float time)
{
    float3 color = float3(0.0, 0.0, 0.0);
    
    // 使用不同的数学函数创建图案
    float pattern1 = sin(uv.x * 10.0 + time) * 0.5 + 0.5;
    float pattern2 = cos(uv.y * 8.0 + time * 0.7) * 0.5 + 0.5;
    float pattern3 = tan(uv.x * 5.0 + uv.y * 5.0 + time) * 0.3 + 0.5;
    
    // 使用pow函数增强对比度
    pattern1 = pow(pattern1, 2.0);
    pattern2 = pow(pattern2, 1.5);
    
    // 使用smoothstep创建平滑过渡
    pattern3 = smoothstep(0.3, 0.7, pattern3);
    
    color.r = pattern1;
    color.g = pattern2;
    color.b = pattern3;
    
    return color;
}

// 使用插值函数混合颜色
float3 blendWithMath(float3 colorA, float3 colorB, float2 uv, float time)
{
    // 使用lerp进行线性插值
    float blendFactor = sin(time + uv.x * 5.0) * 0.5 + 0.5;
    float3 lerpedColor = lerp(colorA, colorB, blendFactor);
    
    // 使用smoothstep创建非线性插值
    float smoothFactor = smoothstep(0.2, 0.8, uv.y);
    float3 finalColor = lerp(lerpedColor, colorA, smoothFactor);
    
    return finalColor;
}

VertexOutput main(VertexInput input)
{
    VertexOutput output;
    
    // 应用波浪效果到位置
    float time = _Time.y; // 假设有时间 uniform
    float3 wavePos = applyWaveEffect(input.position.xyz, time);
    output.position = float4(wavePos, input.position.w);
    
    // 使用数学函数创建动态颜色模式
    float3 mathColor = createMathPattern(input.texcoord, time);
    
    // 混合原始颜色和数学模式颜色
    float3 baseColor = input.color.rgb;
    float3 finalColor = blendWithMath(baseColor, mathColor, input.texcoord, time);
    
    output.color = float4(finalColor, input.color.a);
    output.texcoord = input.texcoord;
    
    return output;
}

C++应用程序代码:


#include <GL/glew.h>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#include <iostream>
#include <ctime>

CGcontext context;
CGprogram vertexProgram;
CGparameter timeParam;

// 获取当前时间(秒)
float getCurrentTime() {
    static clock_t start = clock();
    return float(clock() - start) / CLOCKS_PER_SEC;
}

void initCg() {
    context = cgCreateContext();
    
    vertexProgram = cgCreateProgramFromFile(context, CG_SOURCE, 
                                           "math_functions.cg", CG_PROFILE_VP20, NULL, NULL);
    if (vertexProgram) {
        cgGLLoadProgram(vertexProgram);
        
        // 获取时间参数(需要在着色器中声明为uniform)
        timeParam = cgGetNamedParameter(vertexProgram, "time");
        if (!timeParam) {
            std::cout << "注意: 着色器中没有找到'time'参数,使用默认值" << std::endl;
        }
        
        std::cout << "数学函数示例程序加载成功" << std::endl;
    }
}

void display() {
    glClearColor(0.05f, 0.05f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    
    // 设置时间uniform
    if (timeParam) {
        float currentTime = getCurrentTime();
        cgSetParameter1f(timeParam, currentTime);
    }
    
    cgGLBindProgram(vertexProgram);
    cgGLEnableProfile(CG_PROFILE_VP20);
    
    // 创建网格几何体
    const int gridSize = 20;
    const float spacing = 0.1f;
    
    glBegin(GL_QUADS);
    for (int i = 0; i < gridSize - 1; i++) {
        for (int j = 0; j < gridSize - 1; j++) {
            float x1 = (i - gridSize/2) * spacing;
            float x2 = (i + 1 - gridSize/2) * spacing;
            float z1 = (j - gridSize/2) * spacing;
            float z2 = (j + 1 - gridSize/2) * spacing;
            
            // 计算基于位置的色彩
            float r = (float)i / (gridSize - 1);
            float g = (float)j / (gridSize - 1);
            float b = 0.5f;
            
            glColor3f(r, g, b);
            
            // 四个顶点
            glTexCoord2f(0.0f, 0.0f); glVertex3f(x1, 0.0f, z1);
            glTexCoord2f(1.0f, 0.0f); glVertex3f(x2, 0.0f, z1);
            glTexCoord2f(1.0f, 1.0f); glVertex3f(x2, 0.0f, z2);
            glTexCoord2f(0.0f, 1.0f); glVertex3f(x1, 0.0f, z2);
        }
    }
    glEnd();
    
    cgGLDisableProfile(CG_PROFILE_VP20);
    glutSwapBuffers();
}

void idle() {
    glutPostRedisplay();
}

void reshape(int width, int height) {
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (double)width/height, 0.1, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(2.0, 3.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Cg数学函数应用示例 - 动态波浪网格");
    
    glewInit();
    initCg();
    
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutIdleFunc(idle);
    
    std::cout << "演示Cg数学函数使用 - 观察动态波浪和颜色模式效果" << std::endl;
    
    glutMainLoop();
    return 0;
}

实例分析:
这个示例全面展示了Cg数学函数的应用。我们使用了:

三角函数(sin, cos, tan)创建波浪效果和动态图案插值函数(lerp, smoothstep)实现颜色混合和平滑过渡幂函数(pow)调整颜色对比度时间变量创建动画效果

C++应用程序创建了一个网格平面,通过着色器中的数学函数实现了动态的波浪变形和颜色变化,展示了数学函数在图形效果创建中的强大能力。

© 版权声明

相关文章

暂无评论

none
暂无评论...