草庐IT

games101 作业3

TYayyyyy 2023-08-24 原文

遇到的问题:

1.项目才打开时无法运行。

解决方法:切换成c++17

解决方法引用:Games101 作业3 环境问题 - 知乎

 注:知乎里面的关于越界限制的控制不适用,虽然可以解决部分作业的问题,但是在bump里面依然会出现越界错误。应该用以下大佬的代码。

 2.出现越界错误

 解决方法:在头文件里面的texture.hpp里面增加限制

    Eigen::Vector3f getColor(float u, float v)
    {
        //以下两句为新增
        u = fmin(1, fmax(u, 0));
        v = fmin(1, fmax(v, 0));

        auto u_img = u * width;
        auto v_img = (1 - v) * height;
        auto color = image_data.at<cv::Vec3b>(v_img, u_img);
        return Eigen::Vector3f(color[0], color[1], color[2]);
    }

解决方法引用:【GAMES101】作业3(提高)与法线贴图原理和渲染管线框架分析

3.如何输入output.png normal这些。

作业分析:

1. 修改函数 rasterize_triangle(const Triangle& t) in rasterizer.cpp: 在此处实现与作业 2 类似的插值算法,实现法向量、颜色、纹理颜色的插值。

        依然逐个像素判断是否在三角形内。前面代码都和作业2一样,只是在for循环内增加法向量、颜色、纹理颜色的插值。插值的方法框架也已经写好了,直接用就是。

(这点确实没想到。。想了很久该怎么用,最后还是参考了别人的才知道。)

static Vector3f interpolate(float alpha, float beta, float gamma, const Vector3f& vert1, const Vector3f& vert2, const Vector3f& vert3, float weight)
{
    return (alpha * vert1 + beta * vert2 + gamma * vert3) / weight;
}

static Vector2f interpolate(float alpha, float beta, float gamma, const Vector2f& vert1, const Vector2f& vert2, const Vector2f& vert3, float weight)
{
    auto u = (alpha * vert1[0] + beta * vert2[0] + gamma * vert3[0]);
    auto v = (alpha * vert1[1] + beta * vert2[1] + gamma * vert3[1]);

    u /= weight;
    v /= weight;

    return Vector2f(u, v);
}

知道了直接调用interpolate实现插值,那其他也没啥难度了,老师代码都给全了。

2. 修改函数 get_projection_matrix() in main.cpp: 将你自己在之前的实验中实现的投影矩阵填到此处,此时你可以运行 ./Rasterizer output.png normal来观察法向量实现结果。

        没什么疑问,直接把作业2的内容放上去就行了。

3. 修改函数 phong_fragment_shader() in main.cpp: 实现 Blinn-Phong 模型计算 Fragment Color.

 需要用到的:

dot()点乘

Norm()求向量长度

Normalized()求标准向量

pow(x,n)求n次方

cwiseProduct()向量对应值相乘。举个例子就是:

一个很好的解释:Blinn-Phong光照模型从定义到实现,一文就够了(1.5w字) - 知乎

4. 修改函数 texture_fragment_shader() in main.cpp: 在实现 Blinn-Phong的基础上,将纹理颜色视为公式中的 kd,实现 Texture Shading Fragment Shader.

 5.修改函数 bump_fragment_shader() in main.cpp: 在实现 Blinn-Phong 的 基础上,仔细阅读该函数中的注释,实现 Bump mapping.
按照老师给的注释挨着写上去,只是写上去没什么难度,难度在于理解代码。

6.修改函数 displacement_fragment_shader() in main.cpp: 在实现 Bump mapping 的基础上,实现 displacement mapping.

5是实现凹凸贴图,6是实现置换贴图。代码只有一句需要修改,就是把point的位置实现真正的改变。老师的备注也是很详细的给出了。

 

代码部分

1.

void rst::rasterizer::rasterize_triangle(const Triangle& t, const array<Vector3f, 3>& view_pos) 
{
    auto v = t.toVector4();
//    std::tuple<float, float, float> angle(alpha, beta, gamma);
    float xmin = v[0][0] < (v[1][0] < v[2][0] ? v[1][0] : v[2][0]) ? v[0][0] : (v[1][0] < v[2][0] ? v[1][0] : v[2][0]);//最左边边界,最小值
    float xmax = v[0][0] > (v[1][0] > v[2][0] ? v[1][0] : v[2][0]) ? v[0][0] : (v[1][0] > v[2][0] ? v[1][0] : v[2][0]);//最右边边界,最大值
    float ymin = v[0][1] < (v[1][1] < v[2][1] ? v[1][1] : v[2][1]) ? v[0][1] : (v[1][1] < v[2][1] ? v[1][1] : v[2][1]);//最矮,最小值
    float ymax = v[0][1] > (v[1][1] > v[2][1] ? v[1][1] : v[2][1]) ? v[0][1] : (v[1][1] > v[2][1] ? v[1][1] : v[2][1]);//最高,最大值

    int x_min = floor(xmin);
    int x_max = ceil(xmax);
    int y_min = floor(ymin);
    int y_max = ceil(ymax);
    //遍历所有坐标
    for (int x = x_min; x <= x_max; x++) {
        for (int y = y_min; y <= y_max; y++) {
            if (insideTriangle(x, y, t.v)) {//如果在三角形内
                auto [alpha, beta, gamma] = computeBarycentric2D(x+0.5, y+0.5, t.v);
//               angle = computeBarycentric2D(x+0.5, y+0.5, t.v);
                float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());    //根据质心坐标计算实际的z值
                float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                zp *= Z;

                if (zp < depth_buf[get_index(x, y)]) {
                    depth_buf[get_index(x, y)] = zp;
                    auto interpolated_color=interpolate(alpha,beta,gamma,t.color[0],t.color[1],t.color[2],1);
                    auto interpolated_normal=interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1);
                    auto interpolated_texcoords=interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1);
                    auto interpolated_shadingcoords = interpolate(alpha, beta, gamma, view_pos[0], view_pos[1], view_pos[2],1);
                    fragment_shader_payload payload(interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
                    payload.view_pos = interpolated_shadingcoords;
                    auto pixel_color = fragment_shader(payload);
                    Vector2i nowpixel(x, y);
                    set_pixel(nowpixel, pixel_color);
                }
            }
        }
    }
    // TODO: From your HW3, get the triangle rasterization code.
    // TODO:从作业3,得到三角形光栅化代码
    // TODO: Inside your rasterization loop:
    // TODO:写入你的光栅化loop循环

    //    * v[i].w() is the vertex view space depth value z.
    //    * v[i].w()是顶点视图空间的深度值z

    //    * Z is interpolated view space depth for the current pixel
    //    * Z是当前像素的插值视图空间深度

    //    * zp is depth between zNear and zFar, used for z-buffer
    //    * zp是zNear和zFar之间的深度,用于z缓冲区

    // float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
    // float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
    // zp *= Z;

    // TODO: Interpolate the attributes:
    // TODO:插入属性:
    // auto interpolated_color 
    // auto interpolated_normal 
    // auto interpolated_texcoords 
    // auto interpolated_shadingcoords 

    // Use: fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
    // Use: payload.view_pos = interpolated_shadingcoords;
    // Use: Instead of passing the triangle's color directly to the frame buffer, pass the color to the shaders first to get the final color;
    // 用法:不要将三角形的颜色直接传递到帧缓冲区,而是首先将颜色传递给着色器以获得最终颜色;
    // Use: auto pixel_color = fragment_shader(payload);

 
}

2.

Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
    // TODO: Use the same projection matrix from the previous assignments
    Matrix4f projection = Matrix4f::Identity();
    Matrix4f persp;
    Matrix4f ortho1, ortho2;

    float r, l, t, b, n, f;
    float angle = eye_fov * MY_PI / 180.0 / 2;
    n = zNear;
    f = zFar;
    t = -tan(angle) * n;
    b = tan(angle) * n;
    r = t * aspect_ratio;
    l = -t * aspect_ratio;

    persp << n, 0, 0, 0,
        0, n, 0, 0,
        0, 0, n + f, -n * f,
        0, 0, 1, 0;

    ortho1 << 2 / (r - l), 0, 0, 0,
        0, 2 / (t - b), 0, 0,
        0, 0, 2 / (n - f), 0,
        0, 0, 0, 1;

    ortho2 << 1, 0, 0, -(r + l) / 2,
        0, 1, 0, -(t + b) / 2,
        0, 0, 1, -(n + f) / 2,
        0, 0, 0, 1;

    projection = ortho1 * ortho2 * persp * projection;
    return projection;

}

运行结果:

3.

Vector3f phong_fragment_shader(const fragment_shader_payload& payload)
{
    Vector3f ka = Vector3f(0.005, 0.005, 0.005);
    Vector3f kd = payload.color;
    Vector3f ks = Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    vector<light> lights = {l1, l2};
    Vector3f amb_light_intensity{10, 10, 10};
    Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Vector3f color = payload.color;
    Vector3f point = payload.view_pos;
    Vector3f normal = payload.normal;

    Vector3f result_color = {0, 0, 0};
    
    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
        //TODO:对于代码中的每个光源,计算* ambient* 、* diffuse* 和* specular* 的值
        //然后在*result_color*对象上累积该结果。
        Vector3f light_intensity = light.intensity / (light.position - point).dot(light.position - point);//I/r^2
        Vector3f l = (light.position - point).normalized();//阳光入射角度
        Vector3f v = (eye_pos - point).normalized();//人视觉角度
        Vector3f h = (l + v).normalized();
        float ld_energy = l.dot(normal) > 0 ? l.dot(normal) : 0;//max(0,n·l)
        float ls_energy = pow((h.dot(normal) > 0 ? h.dot(normal) : 0), p);//max(0,n·h)^p
        Vector3f la = ka.cwiseProduct(amb_light_intensity);
        Vector3f ld = kd.cwiseProduct(light_intensity) * ld_energy;
        Vector3f ls = ks.cwiseProduct(light_intensity) * ls_energy;
        result_color += la + ld + ls;
    }

    return result_color * 255.f;
}

运行结果:

 

4.

Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{
    Vector3f return_color = {0, 0, 0};
    if (payload.texture)
    {
        // TODO: Get the texture value at the texture coordinates of the current fragment
        //获取当前片段纹理坐标处的纹理值
        return_color = payload.texture->getColor(payload.tex_coords.x(),payload.tex_coords.y());
    }
    Vector3f texture_color;
    texture_color << return_color.x(), return_color.y(), return_color.z();

    Vector3f ka = Vector3f(0.005, 0.005, 0.005);
    Vector3f kd = texture_color / 255.f;
    Vector3f ks = Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    vector<light> lights = {l1, l2};
    Vector3f amb_light_intensity{10, 10, 10};
    Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Vector3f color = texture_color;
    Vector3f point = payload.view_pos;
    Vector3f normal = payload.normal;

    Vector3f result_color = {0, 0, 0};

    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
//以下代码和phong没有区别
        Vector3f light_intensity = light.intensity / (light.position - point).dot(light.position - point);//I/r^2
        Vector3f l = (light.position - point).normalized();//阳光入射角度,标准向量
        Vector3f v = (eye_pos - point).normalized();//人视觉角度,标准向量
        Vector3f h = (l + v).normalized();
        float ld_energy = l.dot(normal) > 0.0 ? l.dot(normal) : 0.0;//max(0,n·l)
        float ls_energy = pow((h.dot(normal) > 0.0 ? h.dot(normal) : 0.0), p);//max(0,n·h)^p
        Vector3f la = ka.cwiseProduct(amb_light_intensity);
        Vector3f ld = kd.cwiseProduct(light_intensity) * ld_energy;
        Vector3f ls = ks.cwiseProduct(light_intensity) * ls_energy;
        result_color += la + ld + ls;
    }

    return result_color * 255.f;
}

运行结果:
5.

Vector3f bump_fragment_shader(const fragment_shader_payload& payload)
{

    Vector3f ka = Vector3f(0.005, 0.005, 0.005);
    Vector3f kd = payload.color;
    Vector3f ks = Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{ {20, 20, 20}, {500, 500, 500} };
    auto l2 = light{ {-20, 20, 0}, {500, 500, 500} };

    vector<light> lights = { l1, l2 };
    Vector3f amb_light_intensity{ 10, 10, 10 };
    Vector3f eye_pos{ 0, 0, 10 };

    float p = 150;

    Vector3f color = payload.color;
    Vector3f point = payload.view_pos;
    Vector3f normal = payload.normal;


    float kh = 0.2, kn = 0.1;

    // TODO: Implement bump mapping here
    //在此处实现bump贴图

    // Let n = normal = (x, y, z)
    float x = normal.x();
    float y = normal.y();
    float z = normal.z();
    // Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
    Vector3f t(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
    // Vector b = n cross product t
    //b=n差积t
    Vector3f b = normal.cross(t);
    // Matrix TBN = [t b n]
    Matrix3f TBN;
    TBN << t.x(), b.x(), x,
        t.y(), b.y(), y,
        t.z(), b.z(), z;
    // dU = kh * kn * (h(u+1/w,v)-h(u,v))
    //bump mapping 部分的 h(u,v)=texture_color(u,v).norm, 其中 u,v 是 tex_coords, w,h 是 texture 的宽度与高度
    //实现 h(u+1/w,v) 的时候要写成 h(u+1.0/w,v)
    float u = payload.tex_coords.x();
    float v = payload.tex_coords.y();
    float w = payload.texture->width;
    float h = payload.texture->height;
    float dU = kh * kn * (payload.texture->getColor(u + 1.0 / w, v).norm() - payload.texture->getColor(u, v).norm());
    // dV = kh * kn * (h(u,v+1/h)-h(u,v))
    float dV = kh * kn * (payload.texture->getColor(u, v + 1.0 / h).norm() - payload.texture->getColor(u, v).norm());
    // Vector ln = (-dU, -dV, 1)
    Vector3f ln(-dU, -dV, 1.0);
    // Normal n = normalize(TBN * ln)
    normal = (TBN * ln).normalized();


    Vector3f result_color = {0, 0, 0};
    result_color = normal;

    return result_color * 255.f;
}

运行结果:

6.

Vector3f displacement_fragment_shader(const fragment_shader_payload& payload)
{
    
    Vector3f ka = Vector3f(0.005, 0.005, 0.005);
    Vector3f kd = payload.color;
    Vector3f ks = Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    vector<light> lights = {l1, l2};
    Vector3f amb_light_intensity{10, 10, 10};
    Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Vector3f color = payload.color; 
    Vector3f point = payload.view_pos;
    Vector3f normal = payload.normal;

    float kh = 0.2, kn = 0.1;
    
    // TODO: Implement displacement mapping here
    // Let n = normal = (x, y, z)
    float x = normal.x();
    float y = normal.y();
    float z = normal.z();
    // Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
    Vector3f t(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
    // Vector b = n cross product t
    Vector3f b = normal.cross(t);
    // Matrix TBN = [t b n]
    Matrix3f TBN;
    TBN << t.x(), b.x(), x,
        t.y(), b.y(), y,
        t.z(), b.z(), z;
    // dU = kh * kn * (h(u+1/w,v)-h(u,v))
    float u = payload.tex_coords.x();
    float v = payload.tex_coords.y();
    float w = payload.texture->width;
    float h = payload.texture->height;
    float dU = kh * kn * (payload.texture->getColor(u + 1.0 / w, v).norm() - payload.texture->getColor(u, v).norm());
    // dV = kh * kn * (h(u,v+1/h)-h(u,v))
    float dV = kh * kn * (payload.texture->getColor(u, v + 1 / h).norm() - payload.texture->getColor(u, v).norm());
    // Vector ln = (-dU, -dV, 1)
    Vector3f ln(-dU, -dV, 1);
    // Position p = p + kn * n * h(u,v)
    //---------------------唯一需要修改的代码-----------------------------
    point += kn * normal * payload.texture->getColor(u, v).norm();
    // Normal n = normalize(TBN * ln)
    normal = (TBN * ln).normalized();


    Vector3f result_color = {0, 0, 0};

    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
        Vector3f light_intensity = light.intensity / (light.position - point).dot(light.position - point);//I/r^2
        Vector3f l = (light.position - point).normalized();//阳光入射角度,标准向量
        Vector3f v = (eye_pos - point).normalized();//人视觉角度,标准向量
        Vector3f h = (l + v).normalized();
        float ld_energy = l.dot(normal) > 0.0 ? l.dot(normal) : 0.0;//max(0,n·l)
        float ls_energy = pow((h.dot(normal) > 0.0 ? h.dot(normal) : 0.0), p);//max(0,n·h)^p
        Vector3f la = ka.cwiseProduct(amb_light_intensity);
        Vector3f ld = kd.cwiseProduct(light_intensity) * ld_energy;
        Vector3f ls = ks.cwiseProduct(light_intensity) * ls_energy;
        result_color += la + ld + ls;

    }

    return result_color * 255.f;
}

运行结果 :

 

 

存在问题:

1.对于bump虽然按照老师的注释写下来了,但是还是不太能理解。希望后期可以再看看

2.这里的weight不知道为什么是1,但是根据我的运行,会存在溢出,必须修改老师给好的框架,我认为这里应该还有可以思考的地方。

static Vector2f interpolate(float alpha, float beta, float gamma, const Vector2f& vert1, const Vector2f& vert2, const Vector2f& vert3, float weight)

 3.光线的平方必须要点乘,而不能先cwiseProduct求平方再求长度。写成这样的话,会比要求得到的图形亮上一点。

Vector3f light_intensity = light.intensity / (light.position - point).cwiseProduct(light.position - point).norm();

左边是老师要求的。右边是我得到的错误结果。

4.

auto [alpha, beta, gamma] = computeBarycentric2D(x+0.5, y+0.5, t.v);

最开始老师给的代码一直有问题,只能自己重写:

float alpha, beta, gamma;
std::tuple<float, float, float> angle(alpha, beta, gamma);
angle = computeBarycentric2D(x+0.5, y+0.5, t.v);

但是会导致法向量模型有问题,只能又改回老师的代码(这时候又没问题了。可能是c++17的原因,之前用的是老版本)。自己重写的会导致结果变为:

 

 

有关games101 作业3的更多相关文章

  1. ruby-on-rails - 在所有延迟的作业之前 Hook - 2

    是否可以在所有delayed_job任务之前运行一个方法?基本上,我们试图确保每个运行delayed_job的服务器都有我们代码的最新实例,所以我们想运行一个方法来在每个作业运行之前检查它。(我们已经有了“check”方法并在别处使用它。问题只是关于如何从delayed_job中调用它。) 最佳答案 现在有一种官方方法可以通过插件来做到这一点。这篇博文通过示例清楚地描述了如何执行此操作http://www.salsify.com/blog/delayed-jobs-callbacks-and-hooks-in-rails(本文中描述

  2. 【Unity大气散射】GAMES104:3A中如何实现大气散射 - 2

    写在前面前两天学习并整理的大气散射基础知识:【Unity大气渲染】关于单次大气散射的理论知识,收获了很多,但不得不承认的是,这其实已经是最早的、90年代的非常古老的方法了,后来也出现了一些优化性的计算思路和方法。因此,我打算先不急着跟各种教程在Unity中实现大气散射,而是再花时间来看看最近的游戏是如何去实现大气渲染的:06.游戏中地形大气和云的渲染(下)|GAMES104-现代游戏引擎:从入门到实践接下来就跟着GAMES104讲地形大气和云渲染的部分学习并做简单的记录,涉及到之前没提到的Mie散射也只选择直接截图PPT的方式记录啦!毕竟对于做作品来说,之后实现出来才是重要的~当然,May佬的

  3. ruby - 脚本在命令行中成功执行但不是作为 cron 作业 - 2

    我有一个bash脚本,它运行一个ruby​​脚本来获取我的Twitter提要。##/home/username/twittercron#!/bin/bashcd/home/username/twitterrubytwitter.rbfriends命令行运行成功/home/username/twittercron但是当我尝试将它作为cronjob运行时,它运行了但无法获取提要。##crontab-e*/15*****/home/username/twittercron脚本已经chmod+x。不知道为什么会这样。有什么想法吗? 最佳答案

  4. ruby - 在 RVM env 的 Ruby cron 作业中找不到 Gem - 2

    我正在尝试在RVM环境中运行10.5的旧PPC机器上运行一个简单的ruby​​脚本。在SO上搜索,我遵循了这个post中选择的答案.这是cron中的结果行:SHELL=/bin/bash00****BASH_ENV=~/.bash_profile&&/bin/bash-c'~/deggy/onlineGW.rb'此命令在用户sam的根目录下的Bash中运行良好。这是我脚本的重要部分:#!/usr/bin/envrubyrequire'open-uri'require'nokogiri'...这是cron的错误输出:X-Cron-Env:X-Cron-Env:X-Cron-Env:X-C

  5. ruby-on-rails - delayed_job 作为守护进程运行时作业失败。使用 rake 作业时运行良好 :work - 2

    我实在是无计可施了。我不明白为什么它不起作用。我创建了一个类,我使用rake命令对其进行调用和排队。当我使用“rakejobs:work”运行worker并调用命令“rakeget_updates”时,它执行得很好。但是,当我将worker作为守护进程运行时(RAILS_ENV=productionbin/delayed_jobstart)并调用命令“rakeget_updates”时,它会产生错误。app/workers/get_updates.rbclassGetUpdatesdefperformbeginning=Time.nowincludeSoapHelperrequire'

  6. ruby-on-rails - 如何将自定义 delayed_job 作业与 ActiveJob 一起使用? - 2

    我正在使用DelayedJob,我想更新我的Rails4.2应用程序以使用ActiveJob。问题是我有一堆看起来像这样的自定义作业:AssetDeleteJob=Struct.new(:user_id,:params)dodefperform#codeend#moremethodsn'stuffend然后在某处的Controller中,作业使用以下语法排队:@asset_delete_job=AssetDeleteJob.new(current_admin_user.id,params)Delayed::Job.enqueue@asset_delete_job我想找到ActiveJo

  7. ruby-on-rails - rails3 中 cron 作业的解决方案 - 2

    我尝试每天在我的Rails应用程序中自动记录一些数据。我想知道是否有人知道一个好的解决方案?我找到了https://github.com/javan/whenever,但我想确保在选择之前了解所有选项。谢谢!艾略特 最佳答案 我真的很喜欢whenever-这是一个很棒的Gem,我已经在生产中使用了它。关于它还有一个很好的Railscasts插曲:http://railscasts.com/episodes/164-cron-in-ruby 关于ruby-on-rails-rails3中c

  8. ruby - 如何在特定队列中推送作业并使用 sidekiq 限制工作人员数量? - 2

    我知道我们可以做到:sidekiq_optionsqueue:"Foo"但在这种情况下,Worker只分配给一个队列:“Foo”。我需要在特定队列中分配作业(而不是worker)。使用Resque很容易:Resque.enqueue_to(queue_name,my_job)另外,为了并发问题,我需要限制每个队列的Worker数量为1。我该怎么做? 最佳答案 您可能会使用https://github.com/brainopia/sidekiq-limit_fetch然后:Sidekiq::Client.push({'class'=>

  9. ruby - 为什么 ruby​​game 和 gosu 比纯 opengl 慢? - 2

    我正在寻找一个好的图形框架来用Ruby制作一个漂亮的2D游戏。我做了3个非常简单的测试,看看哪个图形Ruby框架在Gosu之间更快。和Rubygame.该测试创建了1000个“Square”类的实例,这些实例使用框架的方法以最简单的方式移动和绘制一个红色方block。第三个测试是同一件事,但在纯OpenGL实现中(没有任何框架)。这是结果:纯OPENGL(使用ruby​​-opengl)80Fps:alttexthttp://grab.by/JTMGOSU(使用ruby​​-opengl+gosu)46Fps:alttexthttp://grab.by/JTCRUBYGAME(使用ru

  10. 网页设计期末作业,基于HTML+CSS+JavaScript超酷超炫的汽车类企业网站(6页) - 2

    🎉精彩专栏推荐💭文末获取联系✍️作者简介:一个热爱把逻辑思维转变为代码的技术博主💂作者主页:【主页——🚀获取更多优质源码】🎓web前端期末大作业:【📚毕设项目精品实战案例(1000套)】🧡程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作(110套)】🌎超炫酷的Echarts大屏可视化源码:【🔰Echarts大屏展示大数据平台可视化(150套)】🔖HTML+CSS+JS实例代码:【🗂️5000套HTML+CSS+JS实例代码(炫酷代码)继续更新中…】🎁免费且实用的WEB前端学习指南:【📂web前端零基础到高级学习视频教程120G干货分享】🥇关于作者:💬历任研发工程师,技术组长,教学总监;

随机推荐