第七章 opengl之光照(基础光照)

【OpenGL学习】光源 实现了定向光、点光源、和聚光灯的效果 阅读详情

基础光照

主要需要理解一个模型是冯氏光照模型,主要结构由3个分量组成:环境,漫反射,镜面光照。下面分别描述下这三个光照:
环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

环境光照

光是来自分散的很多光源,其中的一个属性是:可以向很多方向发散并反弹,从而能够到达不是非常直接临近的点。所以,光能够在它的表面上反射,对一个物体产生间接的影响。考虑到这种情况的算法是全局照明算法,算法开销高昂又极其复杂。

使用一个简化的全局照明模型,也就是环境光照。用一个很小的常量(光照)颜色,添加到物体片段的最终颜色中,这样的话,即使场景中没有直接的光源也能看到好像存在一些发散的光。
把环境光照添加到场景中:用光的颜色乘以一个很小的常量环境因子,再乘以物体的颜色,最终的结果作为片段的颜色:

void main()
{
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    vec3 result = ambient * objectColor;
    FragColor = vec4(result, 1.0);
}

漫反射光照

漫反射光照使得物体上与光线方向越接近的片段能够从光源出获得更多的亮度。如果光线垂直于物体表面,则这束光对物体的影响会最大化,为了测量光线和片段的角度,需要使用一个法向量:是垂直于片段表面的一个向量。
注意:两个单位向量的夹角越小,它们点乘的结果越倾向于1,当两个向量的夹角为90°,则点乘会变成0。(为了得到两个向量夹角的余弦值,需要使用单位向量,则需要保证所有的向量都是标准化的,否则点乘返回的就不仅仅是余弦值了)

首先需要先求得法向量:
由于顶点本身没有表面,需要利用它周围的顶点来计算出这个顶点的表面。可以使用叉乘对立方体所有的顶点计算法向量。由于3D立方体不是一个复杂的图形,那么可以简单的把法线数据手工添加到顶点数据中。

由于向顶点数组添加了额外的数据,所以需要更新光照的顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
...

现在向每个顶点都添加了一个法向量并且更新了顶点着色器,还需要更新顶点属性指针。(注意:灯使用同样的顶点数组作为它的顶点数据,然后灯的着色器并没有使用新添加的法向量。所以不需要更新灯的着色器或者是属性的配置,但是必须修改顶点属性指针来适应新的顶点数组大小):

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

如果只想使用每个顶点的前三个float,并且忽略后3个float,则只需要把步长参数改成float大小的6倍即可。

所有的光照计算都是在片段着色器中进行的,需要把法向量由顶点着色器传递到片段着色器:

out vec3 Normal;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    Normal = aNormal;
}

接下来,在片段着色器中定义相应的输入变量:

in vec3 Normal;

下面需要计算漫反射光照。当前知道了每个顶点的法向量,但是还需要知道光源的位置向量和片段的位置向量,由于光源的位置是一个静态变量,那么可以简单的在片段着色器中把它声明成uniform:

uniform vec3 lightPos;

然后再渲染循环中不断更新uniform,使用上述声明的lightPos向量作为光源位置:

lightingShader.setVec3("lightPos", lightPos);

最后需要片段的位置。会再世界空间中进行所有光照计算,所以需要一个再世界空间中的顶点位置。可以通过顶点位置属性乘以模型矩阵来变换到世界空间坐标。这个操作在顶点着色器中完成。下面实操,声明一个输出变量,并计算它的世界空间坐标:

out vec3 FragPos;  
out vec3 Normal;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = aNormal;
}

最后在片段着色器中添加相应的输入变量:

in vec3 FragPos;

综上,所有的变量都设置完成,下来可以在片段着色器中添加光照计算。

首先需要计算光源和片段位置之间的方向向量。注:光的方向向量是光源位置向量 和 片段位置向量 之间的向量差。需要确保所有相关向量最后都转换成单位向量,需要把法线和最终的方向向量都进行标准化:

vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);

下面需要对norm和lightDir向量进行点乘,计算光源对当前片段实际的漫反射影响。结果值再乘以光的颜色,得到漫反射分量。两个向量之间的角度越大,漫反射分量就会越小

float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

如果两个向量之间的角度大于90度,点乘的结果就会变成负数,这样会导致漫反射分量变为负数。为此,我们使用max函数返回两个参数之间较大的参数,从而保证漫反射分量不会变成负数。负数颜色的光照是没有定义的,所以最好避免它,除非你是那种古怪的艺术家。

现在我们有了环境光分量和漫反射分量,我们把它们相加,然后把结果乘以物体的颜色,来获得片段最后的输出颜色:

vec3 result = (ambient + diffuse) * objectColor;
FragColor = vec4(result, 1.0);

镜面光照

镜面光照也决定于光的方向向量和物体的法向量,但是它也决定于观察方向,例如玩家是从什么方向看向这个片段的。镜面光照决定于表面的反射特性。如果我们把物体表面设想为一面镜子,那么镜面光照最强的地方就是我们看到表面上反射光的地方

可以根据法向量翻折入射光的方向来得到反射向量。计算反射向量和观察方向的角度差。夹角越小,镜面光的作用越大。效果是:如果看向入射光再表面的反射方向时,会看到一点高光。
观察向量是计算镜面光照时需要的一个额外变量,可以使用观察者的世界空间位置和片段的位置来计算它。再计算出镜面光照强度,用其乘以光照颜色,并将它和环境光照和漫反射光照部分加和。

要得到观察者的世界空间坐标,可以直接使用摄像机的位置向量。把uniform添加到片段着色器中,把摄像机位置传给着色器:

uniform vec3 viewPos;
lightingShader.setVec3("viewPos", camera.Position);

下来可以计算高光强度。首先定义一个镜面强度变量,给镜面高光一个中等亮度颜色:

float specularStrength = 0.5;

下一步,计算视线方向向量,和对应的沿着法线轴的反射向量:

vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);

需要注意的是我们对lightDir向量进行了取反。reflect函数要求第一个向量是从光源指向片段位置的向量,但是lightDir当前正好相反,是从片段指向光源(由先前我们计算lightDir向量时,减法的顺序决定)。为了保证我们得到正确的reflect向量,我们通过对lightDir向量取反来获得相反的方向。第二个参数要求是一个法向量,所以我们提供的是已标准化的norm向量。

最后需要计算镜面分量:

float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

先计算视线方向与反射方向的点乘(并确保它不是负值),然后取它的32次幂。这个32是高光的反光度(Shininess)。一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小(2,4,6,8,16,32,64,128,256)
把镜面分量加到环境光分量和漫反射分量中,再用结果乘以物体的颜色:

vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);

补充:
在光照着色器的早期,开发者曾经在顶点着色器中实现冯氏光照模型。在顶点着色器中做光照的优势是,相比片段来说,顶点要少得多,因此会更高效,所以(开销大的)光照计算频率会更低。然而,顶点着色器中的最终颜色值是仅仅只是那个顶点的颜色值,片段的颜色值是由插值光照颜色所得来的。结果就是这种光照看起来不会非常真实,除非使用了大量顶点。
在顶点着色器中实现的冯氏光照模型叫做Gouraud着色(Gouraud Shading),而不是冯氏着色(Phong Shading)。记住,由于插值,这种光照看起来有点逊色。冯氏着色能产生更平滑的光照效果。

完整代码如下:

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <learnopengl/shader_m.h>
#include <learnopengl/camera.h>

#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;

// timing
float deltaTime = 0.0f;	
float lastFrame = 0.0f;

// lighting
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // tell GLFW to capture our mouse
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // configure global opengl state
    // -----------------------------
    glEnable(GL_DEPTH_TEST);

    // build and compile our shader zprogram
    // ------------------------------------
    Shader lightingShader("2.2.basic_lighting.vs", "2.2.basic_lighting.fs");
    Shader lightCubeShader("2.2.light_cube.vs", "2.2.light_cube.fs");

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };
    // first, configure the cube's VAO (and VBO)
    unsigned int VBO, cubeVAO;
    glGenVertexArrays(1, &cubeVAO);
    glGenBuffers(1, &VBO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(cubeVAO);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // normal attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);


    // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube)
    unsigned int lightCubeVAO;
    glGenVertexArrays(1, &lightCubeVAO);
    glBindVertexArray(lightCubeVAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // note that we update the lamp's position attribute's stride to reflect the updated buffer data
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);


    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // per-frame time logic
        // --------------------
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // be sure to activate shader when setting uniforms/drawing objects
        lightingShader.use();
        lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
        lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
        lightingShader.setVec3("lightPos", lightPos);
        lightingShader.setVec3("viewPos", camera.Position);

        // view/projection transformations
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        lightingShader.setMat4("projection", projection);
        lightingShader.setMat4("view", view);

        // world transformation
        glm::mat4 model = glm::mat4(1.0f);
        lightingShader.setMat4("model", model);

        // render the cube
        glBindVertexArray(cubeVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);


        // also draw the lamp object
        lightCubeShader.use();
        lightCubeShader.setMat4("projection", projection);
        lightCubeShader.setMat4("view", view);
        model = glm::mat4(1.0f);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube
        lightCubeShader.setMat4("model", model);

        glBindVertexArray(lightCubeVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);


        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &cubeVAO);
    glDeleteVertexArrays(1, &lightCubeVAO);
    glDeleteBuffers(1, &VBO);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{
    float xpos = static_cast<float>(xposIn);
    float ypos = static_cast<float>(yposIn);
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(static_cast<float>(yoffset));
}
OpenGL 光照 只是针对Phong模型的镜面光计算方案进行优化,主要是因为Phong模型镜面光效果是取决于光线反射向量和观察向量的夹角的cos值 max(0, dot(viewDir, reflectDir)),所以这个方案就存在当夹角 >= 90 的时候我们得到得镜面光效果都 等于0 导致会存在镜面光一个明显的断层效果,如下图右图就是观察方向和反射方向夹角 > 90 的情况了。所以可以理解为正常情况下(全局白色光照,全局的含义是光是四面八方全方位的光照)物体颜色就是物体的对颜色的反射系数。由于物体的反射系数是不变的。 阅读详情

相关推荐

OpenGL光源

按照之前简化版点光源生成的 平行光

牵LV小猪精的博客 1303

OpenGL学习 跟着官网教程学习(基础光照)

OpenGL学习 跟着官网教程学习(基础光照)

asiwxy的博客 967

第一章 OpenGL ES 基础-屏幕、纹理、顶点坐标

GL_TRIANGLE_STRIP:指定要绘制的基本图元类型为三角形条带,即每三个顶点定义一个三角形,但从第三个顶点开始,每个新顶点都与前两个顶点一起构成一个新的三角形。坐标系 的 左下点(-1, -1),右下角(1,-1) , 左上角(-1, 1) , 右上角(1 , 1)坐标系 的 左下点 (0, 0),右下角(1 , 0) , 左上角(0, 1 ), 右上角(1, 1)0:指定了要渲染的顶点数组的起始索引,这里表示从顶点数组的第一个顶点开始。使用第二个顶点作为三角形条带的第二个顶点。

u012836015的博客 3536

OpenGL 入门 10:光源

OpenGL 光源

On_The_Way 395

计算机图形学 第七章 光照计算和纹理映射

文章目录Phong模型及Blinn- Phong模型计算公式及相关参数Whitted全局光照模型Gouraud明暗处理和Phong明暗处理原理光线跟踪算法原理辐射度算法原理,理解 辐射度方程纹理空间,纹理坐标,纹理映射传送门 Phong模型及Blinn- Phong模型计算公式及相关参数 Whitted全局光照模型 直接光源项,间接光源项,透射项 Gouraud明暗处理和Phong明暗处理原理 光线跟踪算法原理 辐射度算法原理,理解 辐射度方程 纹理空间,纹理坐标,纹理映射 三角形纹理映射,基本几何体纹理

日天家的猫 1239

基于STM32 HAL库的L298N四驱电机PWM调速与方向控制实战

本文详细介绍了基于STM32 HAL库驱动L298N电机控制板实现四驱小车PWM调速与方向控制的实战方法。涵盖硬件连接、GPIO与定时器配置、PWM生成、方向控制逻辑及电源管理等核心内容,并提供代码优化与常见问题解决方案,帮助开发者快速实现高效稳定的电机控制。

jump7的博客 222

OpenGL第七章 基础光照

这里的基础光照使用的是: 冯氏光照模型 冯氏光照模型分为三部分 1环境光照(Ambient) 把环境光照添加到场景里非常简单。 我们用光的颜色乘以一个很小的常量环境因子,再乘以物体的颜色,然后将最终结果作为片段的颜色 2漫反射(diffuse) 我们知道两个单位向量的夹角越小,它们点乘的结果越倾向于1。 当两个向量的夹角为90度的时候,点乘会变为0。这同样适用于θ,θ越大,光对片段颜色的影响就应该越小。 那么此时我们就要一个法线方向和一个指向光源的方向(图形学里面光照方向指的是从每一个世界坐标出发指向

ahang的博客 689

基于Agent与大模型的性能测试脚本自动化生成实践

性能测试是软件质量保障的关键环节,其核心在于模拟真实用户负载以评估系统在高并发下的表现。传统脚本编写依赖人工,效率低下且易出错。通过引入智能体(Agent)与大语言模型(LLM)技术,可实现从自然语言描述或接口文档到可执行脚本的自动化转换。Agent负责任务规划与工具调用,LLM则提供强大的意图理解与代码生成能力,二者结合能精准解析测试场景、自动处理参数化与接口关联等复杂逻辑。该技术方案显著提升了脚本生成效率与准确性,尤其适用于持续集成、海量接口回归等需要快速构建测试场景的工程实践。本文以JMeter和Lo

weixin_28718769的博客 259

第十一章 OpenGL ES 基础-基础光照

现实世界中的光照是极其复杂,难以计算的,因此OpenGL的光照使用的是简化的模型,其中一个模型被称为冯氏光照模型。1、环境(Ambient)光照2、漫反射(Diffuse)光照3、镜面(Specular)光照。

u012836015的博客 1020

无人驾驶小车调试笔记(六)-- 车轮校准

简介:小车的动力完全来自于两个电机带动的车轮,在理想状态下,给两个电机同样的驱动参数,两个车轮会以同样的转速带动小车直线行驶,而实际情况是每个电机可能都会有个体差异,也就是说给予同样的驱动参数,表现出的转动速度会有些许不同,两个轮子的差速会导致车辆拐弯,后果就是给了直线行驶的控制命令,但是车走着走着就偏离了原本的方向,所以在开始控制之前,我们需要先平衡两个车轮的转速,保证在同样驱动参数下,两个车轮转速大致相同,也就是可以走直线。 车轮校准分为3步,测试、调整后再测试、保存参...

aibingjin的博客 1821

保姆级教程:在PX4 Gazebo仿真中为Iris无人机挂载Intel D435i深度相机(含完整配置流程)

本文提供在PX4 Gazebo仿真环境中为Iris无人机挂载Intel D435i深度相机的完整配置流程,涵盖环境准备、模型部署、插件安装及调试技巧。通过详细步骤和常见问题解决方案,帮助开发者快速搭建深度相机仿真环境,适用于无人机视觉开发与算法测试。

weixin_28688385的博客 338

《OpenGL编程指南》 笔记七 光照与阴影

文章目录第七章 光照与阴影7.1 光照介绍7.2 经典光照模型7.2.1 不同光源类型的片元着色器没有光照环境光方向光点光源聚光灯7.2.2 将计算移到顶点着色器7.2.3 多个光源和材质多个光源材质属性双面光照7.2.4 光照坐标系7.2.5 经典光照模型的局限7.3 光照模型进阶7.3.1 半球光照7.3.2 基于图像的光照7.3.3 球面光照7.4 阴影映射7.4.1 创建一张阴影贴图7.4...

wwhx27的博客 768

实战指南:CentOS 7下VOS3000 8.0从零部署到授权激活全流程

本文提供了一份详尽的CentOS 7系统下部署VOS3000 8.0的实战指南。从环境准备、使用FileZilla上传安装包、执行自动化安装脚本,到获取软件码、利用注册机生成授权文件并激活,手把手带领用户完成从零部署到投入商用的全流程,确保系统稳定运行。

zero1的博客 896

OpenGL基础图形编程 - 特殊光处理

14.1、光照模型  OpenGL光照模型的概念由一下三部分组成:1)全局泛光强度、2)视点位置在景物附近还是在无穷远处、3)物体的正面和背面是否分别进行光照计算。  14.1.1 全局环境光  正如前面基础篇中所提到的一样,每个光源都能对一个场景提供环境光。此外,还有一个环境光,它不来自任何特定的光源,即称为全局环境光。下面用参数GL_LIGHT_MODEL_AMBIENT来说明全局环境光的RG

popkiler的专栏 1914

Power domain 划分该如何下手

假如PD1比PD3更always on的话,我们只需要插入普通buffer即可,因为PD3 on,PD1也肯定是ON,PD1 off,PD3肯定时off,PD1能cover PD3的电源情况,因此使用普通buffer也可,但是处于省电角度,我们还是使用AO buffer;在我们做多power domain时,在切分power domain时,需要谨慎,稍有不慎,就会出现congestion以及timing问题,那问题来了,我们在切分power domain时有什么考究么,今天小编就和大家一起讨论讨论。

芯冰乐 207

对称式目镜设计实例

对称式目镜设计实例

weixin_72050316的博客 1163

第七章 光照与阴影

第七章 光照与阴影 双面光照 OpenGL内置变量gl_FrontFacing设置为true则为正面,否则为反面

lele0503的博客 339

UE4打包为Framework库嵌入iOS原生工程

UE4打包为Framework库嵌入iOS原生工程

tangfuling1991的博客 1294

第三章 OpenGL ES 基础-基础-GLSL渲染纹理

vec4 :是四个分量的向量mat4 :是4x4的浮点矩阵渲染一张图片纹理写简单的shader在 OpenGL 中,线程调用 GLSL(OpenGL Shading Language)时通常需要注意一些关联问题,以确保正确的操作和流程。GLSL 着色器编译在一个线程中加载、编译和链接 GLSL 着色器程序。确保着色器程序在正确的上下文中创建,并且每个着色器程序都与合适的 OpenGL 上下文相关联。Uniform 和 Attribute 数据传递。

u012836015的博客 1997

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

《OpenGL 编程指南》(OpenGL红皮书) 阅读笔记

在此记录下OpenGL红皮书的学习历程: 第一章:OpenGL简介 第二章:状态管理和绘制几何物体 第三章:视图 第四章:颜色 第五章:光照 第六章:混合、抗锯齿、雾和多边形偏移 第七章:显示列表 第八章:绘制像素、位图、字体和图像 第九章:纹理绘图 第十章:帧缓冲区 第十一章:分格化和二次方程表面 第十二章:求值器和NURBS 第十三章:选择和反馈 第十四章:Open

Name_David_Lee的专栏 2293

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

第七章 OpenGL ES 基础-输入输出框架思维

*思维:**渲染正常以一个FBO为住,把它当显示的一条线用,FBO(A),另外一个FBO把它当绘制各种特效还是其他的大杂烩的一条线用,FBO(B),最后要显示时候FBO(A)和FBO(B)可以交换。

u012836015的博客 951

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

OpenGL特殊光处理

http://www.cppblog.com/zmj/archive/2006/01/17/2818.html   本章内容是基础篇第七章的补充和提高。这一章主要讲述一些特殊光效果处理,如全局环境光、双面光照、光的衰减、聚光、多光源、光源位置的改变等等。读者若有兴趣,可以按照本章例程的方法作出许多变换,则会出现意想不到的效果,充分发挥你的艺术才能。 14.1、光照模型   OpenGL光

shenlan282的笔记 1583

OpenGL4 着色器语言秘籍(一)

OpenGL 着色语言(GLSL)现在是使用 OpenGL 编程的基本和关键部分。它通过使以前固定功能的图形管道可编程,为我们提供了前所未有的灵活性和强大的功能。使用 GLSL,我们可以利用图形处理单元(GPU)实现高级和复杂的渲染技术,甚至进行任意计算。由于细分着色器和计算着色器等着色阶段,使用 GLSL 4.x,程序员可以利用 GPU 做比以前更多的事情。在这本书中,我们涵盖了 GLSL 编程的全谱系。从使用顶点和片段着色器进行着色的基础开始,我们带你从简单到高级技术。

龙哥盟 1385

第四章 OpenGL ES 基础-位移、缩放、旋转原理

对应矩阵效果注意:必须是相同维度矩阵才行,比如23矩阵不能和33矩阵进行加减矩阵与标量数乘和矩阵与标量加减类似, 实际是对矩阵的每个元素和该标量进行相乘,如下图所示所以值其实就是两个矩阵行列式相乘的和,取出矩阵A的i行下的所有元素与矩阵B的j列下的所有元素进行相乘和。

u012836015的博客 1695
上一篇: 第六章 opengl之光照(颜色)
下一篇: 第八章 opengl之光照(材质)
Re_view
博客等级 码龄7年 609粉丝 291原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值