zoukankan      html  css  js  c++  java
  • NeHe OpenGL教程 第四十七课:CG顶点脚本

    转自【翻译】NeHe OpenGL 教程

    前言

    声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢。

    NeHe OpenGL第四十七课:CG顶点脚本

    CG 顶点脚本

    nVidio的面向GPU的C语言,如果你相信它就好好学学吧,同样这里也只是个入门。记住,类似的语言还有微软的HLSL,OpenGL的GLSL,ATI的shaderMonker。不要选错哦:)

    使用顶点和片断脚本去做渲染工作可以得到额外的好处,最大的好处就是把CPU的一些工作交给了GPU,Cg提供了书写这些强大的脚本的一种手段。
    这篇教程有许多目的,第一向你展现了一个非常简单的顶点脚本,第二向你说明如何在OpenGL中使用Cg编写的脚本。

    这个教程是基于最新的NeHeGL的基本代码,为了获得更多的信息,你可以访问nVidia的官方网站(developer.nvidia.com),它会给你一个完整的答案。

    注意:这个教程不是叫你如何去写一个完整的Cg脚本,而是教你在OpenGL中载入并运行脚本。

    开始:
    第一步,从nVidia的网站上下载Cg Compiler库,最好去下载1.1版本的,因为nvidia各个版本的变化很大,为了让程序不出现任何问题,最好这样做,因为我们用的是1.1版本的。

    下一步,包含编译需要的头文件和库文件。

    我已经帮你把它们拷贝到了工程的文件夹里了。

    Cg介绍
    你必须有以下几个概念:
    1、顶点脚本会作用于你输入的每一个顶点,如果你想要作用于一些顶点,那么你必须在作用前加载顶点脚本,并于作用后释放顶点脚本。
    2、顶点脚本输出的结果被送入到片断处理器中,你不用管这其中是如何实现的。
    最后,记住顶点脚本在图元装配前被执行,片断脚本在光栅化后被执行。

    好了,现在我们创建一个空白的文件吧(保存为wave.cg),接着我们创建一个数据结构,它被我们得脚本使用。下面的代码被加入到wave.cg文件中。
     
    struct appdata { float4 position : POSITION; float4 color : COLOR0; float3 wave : COLOR1;};

    上面的结果说明,我们输入的顶点包含一个位置坐标,一个颜色和我们自定义的波的颜色
    下面的代码定义一个输出顶点的数据,包括一个顶点和颜色
     
    struct vfconn{ float4 HPos : POSITION; float4 Col0 : COLOR0;};
      
    下面的代码是Cg的主函数,每个顶点都会被以下函数执行:
     
    vfconn main(appdata IN, uniform float4x4 ModelViewProj)
    {
    vfconn OUT; // 保存我们输出顶点的数据

    // 计算顶点y的坐标
    IN.position.y = ( sin(IN.wave.x + (IN.position.x / 5.0) ) + sin(IN.wave.x + (IN.position.z / 4.0) ) ) * 2.5f;

    // 保存到输出数据中
    OUT.HPos = mul(ModelViewProj, IN.position);

    // 不改变输入的颜色
    OUT.Col0.xyz = IN.color.xyz;

    return OUT;
    }

    完成了上面的代码,记得保存一下.

    下面我们到了程序中,首先包含使用cg需要的头文件,和库文件
     
    #include <cgcg.h>         #include <cgcggl.h>         
    #pragma comment( lib, "cg.lib" )       #pragma comment( lib, "cggl.lib" )       
    下面我们定义一些全局变量,用来计算我们得网格和控制cg程序的开关 
      
    #define  SIZE 64        // 定义网格的大小bool  cg_enable = TRUE, sp;       // 开关Cg程序GLfloat  mesh[SIZE][SIZE][3];       // 保存我们的网格GLfloat  wave_movement = 0.0f;       // 记录波动的移动
      
    下面我们来定义一些cg相关的全局变量

     CGcontext cgContext;        // 用来保存cg脚本

    我们需要的第一个变量是CGcontext,这个变量是多个Cg脚本的容器,一般来说,你获得你可以用函数从这个容器中获得你想要的脚本
    接下来我们定义一个CGprogram变量,它用来保存我们得顶点脚本
     
    CGprogram cgProgram;        // 我们得顶点脚本
      
    接下来我们需要一个变量来设置如何编译这个顶点脚本 
      
    CGprofile cgVertexProfile;       // 被顶点脚本使用

    下面我们需要一些参数用来把Cg脚本使用的数据从程序中传送过去。 
      
    CGparameter position, color, modelViewMatrix, wave;     // 脚本中需要的参数

    在初始化阶段我们先要创建我们网格数据 
      
     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);     
    for (int x = 0; x < SIZE; x++)
    {
    for (int z = 0; z < SIZE; z++)
    {
    mesh[x][z][0] = (float) (SIZE / 2) - x;
    mesh[x][z][1] = 0.0f;
    mesh[x][z][2] = (float) (SIZE / 2) - z;
    }
    }

    我们设置多边形的现实模式为线框图,接着遍历没有顶点,设置其高度。
    接下来,我们初始化Cg程序

    // 设置Cg cgContext = cgCreateContext();       // 创建一个Cg容器
    // 测试是否创建成功
    if (cgContext == NULL)
    {
    MessageBox(NULL, "Failed To Create Cg Context", "Error", MB_OK);
    return FALSE;
    }

    我们创建一个Cg程序的容器,并检查它是否创建成功 
      
    cgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);    // 配置在OpenGL中使用顶点缓存
    // 检测Cg程序的是否创建成功
    if (cgVertexProfile == CG_PROFILE_UNKNOWN)
    {
    MessageBox(NULL, "Invalid profile type", "Error", MB_OK);
    return FALSE;
    }

    cgGLSetOptimalOptions(cgVertexProfile); // 启用配置文件

    如果你想使用片断脚本,使用CG_GL_FRAGMENT变量。如果返回的变量为CG_PROFILE_UNKNOW表示没有可用的配置文件,则不能编译你需要的Cg程序。 
      
    // 从文件中载入Cg程序 cgProgram = cgCreateProgramFromFile(cgContext, CG_SOURCE, "CG/Wave.cg", cgVertexProfile, "main", 0);
    // 检测是否成功
    if (cgProgram == NULL)
    {
    CGerror Error = cgGetError();
    MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);
    return FALSE;
    }

    我们尝试从源文件中创建一个Cg程序,并返回编译后的结果。  
      
    // 载入脚本 cgGLLoadProgram(cgProgram);
      
    下面我们把顶点脚本载入到显存,并准备帮定给GPU。所有的脚本在使用前必须加载。 

     // 把数据变量地址发送给Cg程序 position = cgGetNamedParameter(cgProgram, "IN.position"); color  = cgGetNamedParameter(cgProgram, "IN.color"); wave  = cgGetNamedParameter(cgProgram, "IN.wave"); modelViewMatrix = cgGetNamedParameter(cgProgram, "ModelViewProj");
    return TRUE;

    在初始化的最后,我们必须告诉Cg脚本在那里去获得输入的数据,我们需要把变量的指针传递过去,下面的函数完成了这个功能。
    程序结束时,记得释放我们创建的内容。
     
    cgDestroyContext(cgContext);       
      
    下面的代码使用空格切换是否使用Cg程序 
      
    if (g_keys->keyDown [' '] && !sp) {  sp=TRUE;  cg_enable=!cg_enable; }
    if (!g_keys->keyDown [' '])  sp=FALSE;
      
    现在我们已经完成了所有的准备工作了,到了我们实际绘制网格的地方了,按照惯例我们还是先设置我们得视口。

     gluLookAt(0.0f, 25.0f, -45.0f, 0.0f, 0.0f, 0.0f, 0, 1, 0);
    // 把当前Cg程序的模型变化矩阵告诉当前程序 cgGLSetStateMatrixParameter(modelViewMatrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
      
     上面我们要做的事就是把当前Cg程序的模型变化矩阵告诉当前程序。

    结下来如果使用cg程序,则把顶点的颜色设置为绿色

     // 如果使用Cg程序 if (cg_enable) {  // 使用顶点脚本配置文件  cgGLEnableProfile(cgVertexProfile);          // 帮定到当前的顶点脚本  cgGLBindProgram(cgProgram);  // 设置绘制颜色  cgGLSetParameter4f(color, 0.5f, 1.0f, 0.5f, 1.0f); }
      
    下面我们来绘制我们的网格。    

     // 开始绘制我们的网格 for (int x = 0; x < SIZE - 1; x++) {  glBegin(GL_TRIANGLE_STRIP);  for (int z = 0; z < SIZE - 1; z++)  {   // 设置Wave参数   cgGLSetParameter3f(wave, wave_movement, 1.0f, 1.0f);   //设置输入的顶点   glVertex3f(mesh[x][z][0], mesh[x][z][1], mesh[x][z][2]);     glVertex3f(mesh[x+1][z][0], mesh[x+1][z][1], mesh[x+1][z][2]);    wave_movement += 0.00001f;            if (wave_movement > TWO_PI)             wave_movement = 0.0f;  }  //经过Cg程序处理,进行绘制  glEnd(); }
      
    上面的代码完成具体的绘制操作,对于每一个顶点,我们动态的传入波动系数和输入原始的顶点数据。在绘制开始前,顶点脚本接受所有的顶点数据并处理,接着进行光栅华操作。
    别忘了在绘制完成后,关闭我们启用的顶点脚本,否则在绘制其它的模型时会让你得到不想要的结果。

    if (cg_enable)  
     cgGLDisableProfile(cgVertexProfile);     // 禁用顶点脚本配置文件
    原文及其个版本源代码下载:

    http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=47

     
     
  • 相关阅读:
    【DFS】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem D. Divisibility Game
    【二分】【三分】【计算几何】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem L. Lines and Polygon
    【线段树】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem J. Jedi Training
    【贪心】【后缀自动机】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem E. Enter the Word
    【转载】随机生成k个范围为1-n的随机数,其中有多少个不同的随机数?
    【推导】【贪心】XVII Open Cup named after E.V. Pankratiev Stage 4: Grand Prix of SPb, Sunday, Octorber 9, 2016 Problem H. Path or Coloring
    【枚举】XVII Open Cup named after E.V. Pankratiev Stage 4: Grand Prix of SPb, Sunday, Octorber 9, 2016 Problem D. Cutting Potatoes
    【找规律】【递归】XVII Open Cup named after E.V. Pankratiev Stage 4: Grand Prix of SPb, Sunday, Octorber 9, 2016 Problem F. Doubling
    【贪心】Codeforces Round #436 (Div. 2) D. Make a Permutation!
    【计算几何】【圆反演】计蒜客17314 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 G. Finding the Radius for an Inserted Circle
  • 原文地址:https://www.cnblogs.com/arxive/p/6239555.html
Copyright © 2011-2022 走看看