LearnOpenGL之Shader编程用算法绘画

————————————————————  前序 ———————————————————

         AndroidLearnOpenGL是本博主自己实现的LearnOpenGL练习集合:

        Github地址:GitHub - wangyongyao1989/AndroidLearnOpenGL: OpenGL基础及运用 

        系列文章:

        1、LearnOpenGL之入门基础

        2、LearnOpenGL之3D显示

        3、LearnOpenGL之摄像机

        4、LearnOpenGL之光照

        5、LearnOpenGL之3D模型加载

        6、LearnOpenGL之文字渲染

        7、LearnOpenGL之高级OpenGL(1)

        8、LearnOpenGL之高级OpenGL(2)

        9、LearnOpenGL之骨骼动画

        10、LearnOpenGL之Shader编程用算法绘画

——————————————————————————————————————————

 —————————————————— 效果展示 ————————————————————

GLSL着色器编程算法绘画


————————————————

                       

一、Shader(着色器)及GLSL概览:

        如果你曾经有用计算机绘图的经验,你就知道在这个过程中你需要画一个圆,然后一个长方形,一条线,一些三角形……直到画出你想要的图像。这个过程很像用手写一封信或一本书 —— 都是一系列的指令,需要你一件一件完成。

        Shaders 也是一系列的指令,但是这些指令会对屏幕上的每个像素同时下达。也就是说,你的代码必须根据像素在屏幕上的不同位置执行不同的操作。就像活字印刷,你的程序就像一个 function(函数),输入位置信息,输出颜色信息,当它编译完之后会以相当快的速度运行。

        shaders并行处理:

        想象你的 CPU 是一个大的工业管道,然后每一个任务都是通过这个管道的某些东西 —— 就像一个生产流水线那样。有些任务要比别的大,也就是说要花费更多时间和精力去处理。我们就称它要求更强的处理能力。由于计算机自身的架构,这些任务需要串行;即一次一个地依序完成。现代计算机通常有一组四个处理器,就像这个管道一样运行,一个接一个地处理这些任务,从而使计算机流畅运行。每个管道通常被称为线程

        另一个 GPU 的魔法是特殊数学函数可通过硬件加速。非常复杂的数学操作可以直接被微芯片解决,而无须通过软件。这就表示可以有更快的三角和矩阵运算 —— 和电流一样快。

        GLSL:

        GLSL 代表 openGL Shading Language,openGL 着色语言。

        Uniforms:

        我们知道了 GPU 如何处理并行线程,每个线程负责给完整图像的一部分配置颜色。尽管每个线程和其他线程之间不能有数据交换,但我们能从 CPU 给每个线程输入数据。因为显卡的架构,所有线程的输入值必须统一(uniform),而且必须设为只读。也就是说,每条线程接收相同的数据,并且是不可改变的数据。

        这些输入值叫做 uniform (统一值),它们的数据类型通常为:floatvec2vec3vec4mat2mat3mat4sampler2D and samplerCube。uniform 值需要数值类型前后一致。且在 shader 的开头,在设定精度之后,就对其进行定义。

        GLSL 还有更多惊喜。GPU 的硬件加速支持我们使用角度,三角函数和指数函数。这里有一些这些函数的介绍:sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() 和 clamp()。

        gl_FragCoord:

        就像 GLSL 有个默认输出值 vec4 gl_FragColor 一样,它也有一个默认输入值( vec4 gl_FragCoord )。gl_FragCoord存储了活动线程正在处理的像素屏幕碎片的坐标。有了它我们就知道了屏幕上的哪一个线程正在运转。为什么我们不叫 gl_FragCoord uniform (统一值)呢?因为每个像素的坐标都不同,所以我们把它叫做 varying(变化值)。

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main() {
	vec2 st = gl_FragCoord.xy/u_resolution;
	gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}

        运行shader:

        可以在 editor.thebookofshaders.com/ 上将其作为独立的 Web 应用运行。

         GLSL插件安装:

                以Android Studio为例在Plugins应用市场中搜索glsl: 

               

        安装插件后glsl文件高亮显示及函数的智能补全及跳转应用等功能:

         跳转至builtin.glsl里面有着色器的标准函数:

        builtin内置标准函数的解析网址为:Built-in Variable (GLSL) - OpenGL Wiki

         GLSL ES参考手册

                 OpenGL ES着色器语言GLSL ES参考文档    

二、Shader的造型函数:

        

        smoothstep函数:

        smoothstep函数是GLSL中用于实现平滑插值的函数之一,它可以在指定起止边界的范围内,将一个给定的值进行平滑插值。smoothstep函数在图形渲染中常常用于平滑过渡,例如在着色器计算过程中,将颜色值进行平滑渐变,从而实现渐变效果。

        smoothstep (edge0, edge1, x)

  • edge0:float类型的起始边界;
  • edge1:float类型的终止边界;
  • x:float类型的输入值;
  • 返回值:返回一个float类型的值,这个值的范围在[0, 1]之间,代表了输入值经过平滑插值后的结果。

        代码实现:

float smoothstep (float edge0, float edge1, float x) {
   // Scale, and clamp x to 0..1 range
   x = clamp((x - edge0) / (edge1 - edge0));

   return x * x * (3.0f - 2.0f * x);
}

float clamp(float x, float lowerlimit = 0.0f,
                         float upperlimit = 1.0f) {
  if (x < lowerlimit) return lowerlimit;
  if (x > upperlimit) return upperlimit;
  return x;
}

        下面这个例子实现从黑色到白色的渐变,并画一条红线的着色器代码:

#version 320 es
precision mediump float;

out vec4 FragColor;         //out代表输出至下一个着色器

uniform vec2 u_resolution;  //视窗的分辨率
uniform float u_time;       //传入的时间变量
uniform float uu;

// Plot a line on Y using a value between 0.0-1.0
float plot(vec2 st) {
    //返回一个float类型的值,这个值的范围在[0, 1]之间,
    //代表了输入值经过平滑插值后的结果。
    return smoothstep(0.02f, 0.0f, abs(st.y - st.x));
}

void main() {
    //1.传入每个像素的xy除视窗的分辨率
	vec2 st = gl_FragCoord.xy / u_resolution;
    //2.获取st的x分量,加载uniform中的u_time,
    //得到跟随时间的变换的渐变色
    vec3 color = vec3(st.x,st.x,abs(sin(u_time)));
    // Plot a line
    float pct = plot(st);
    //3.【(1.0f - pct) * color】表示除直线外的渐变颜色,
    //【pct * vec3(1.0f,0.0f,0.0f)】表示附着直线的颜色。
    color = (1.0f - pct) * color + pct * vec3(1.0f,0.0f,0.0f);
	FragColor = vec4(color , 1.0f);
}

        step()函数:

        step()插值函数需要输入两个参数。第一个是极限或阈值,第二个是我们想要检测或通过的值。对任何小于阈值的值,返回 0.0,大于阈值,则返回 1.0

#version 320 es
precision mediump float;

out vec4 FragColor;         //out代表输出至下一个着色器

uniform vec2 u_resolution;  //视窗的分辨率
uniform float u_time;       //传入的时间变量

float plot(vec2 st, float pct){
    return  smoothstep( pct-0.02, pct, st.y) -
    smoothstep( pct, pct+0.02, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution;

    //插值函数需要输入两个参数。第一个是极限或阈值,
    //第二个是我们想要检测或通过的值。对任何小于阈值的值,
    //返回0.0,大于阈值,则返回1.0。
    float y = step(0.5,st.x);

    vec3 color = vec3(y);

    float pct = plot(st,y);
    color = (1.0-pct)*color+pct*vec3(1.0,0.0,0.0);

    FragColor = vec4(color , 1.0f);
}

           其他有用的函数:

#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

float plot(vec2 st, float pct){
  return  smoothstep( pct-0.02f, pct, st.y) -
          smoothstep( pct, pct+0.02f, st.y);
}

void main() {
	vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

//    y = mod(x,0.5); // 返回 x 对 0.5 取模的值
//     y = fract(x); // 仅仅返回数的小数部分
     y = ceil(x);  // 向正无穷取整
    // y = floor(x); // 向负无穷取整
    // y = sign(x);  // 提取 x 的正负号
    // y = abs(x);   // 返回 x 的绝对值
    // y = clamp(x,0.0,1.0); // 把 x 的值限制在 0.0 到 1.0
    // y = min(0.0,x);   // 返回 x 和 0.0 中的较小值
    // y = max(0.0,x);   // 返回 x 和 0.0 中的较大值

    // Plot a line
    float pct = plot(st,y);

    vec3 color = pct * vec3(1.0f,0.0f,0.0f);

	FragColor = vec4(color , 1.0f);
}

         造型函数进阶:

        更加复杂的造型函数的文档:Shaping Functions

  •  多项式造型函数:用于在 [0...1] 范围内塑造、补间和缓和信号的多项式函数。如下例子:
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

float blinnWyvillCosineApproximation (float x){
    float x2 = x*x;
    float x4 = x2*x2;
    float x6 = x4*x2;

    float fa = (4.0/9.0);
    float fb = (17.0/9.0);
    float fc = (22.0/9.0);

    float y = fa*x6 - fb*x4 + fc*x2;
    return y;
}

float doubleCubicSeat (float x, float a, float b){

    float epsilon = 0.00001;
    float min_param_a = 0.0 + epsilon;
    float max_param_a = 1.0 - epsilon;
    float min_param_b = 0.0;
    float max_param_b = 1.0;
    a = min(max_param_a, max(min_param_a, a));
    b = min(max_param_b, max(min_param_b, b));

    float y = 0.00000f;
    if (x <= a){
        y = b - (b * pow(1.0-x/a, 3.0));
    } else {
        y = b + (1.0-b)*pow((x-a)/(1.0-a), 3.0);
    }
    return y;
}

float plot(vec2 st, float pct){
    return smoothstep(pct-0.02f, pct, st.y) -
    smoothstep(pct, pct+0.02f, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    //    y = blinnWyvillCosineApproximation(x);
    y = doubleCubicSeat(x, 5.0f, 5.0f);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}
  • 指数造型函数:指数整形函数的实现中,控制参数a允许设计者将函数从缓出形式改变为缓入形式。如下例子:
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

float exponentialEasing (float x, float a){

    float epsilon = 0.00001;
    float min_param_a = 0.0 + epsilon;
    float max_param_a = 1.0 - epsilon;
    a = max(min_param_a, min(max_param_a, a));

    if (a < 0.5){
        // emphasis
        a = 2.0*(a);
        float y = pow(x, a);
        return y;
    } else {
        // de-emphasis
        a = 2.0*(a-0.5);
        float y = pow(x, 1.0/(1.0-a));
        return y;
    }
}

float plot(vec2 st, float pct){
    return smoothstep(pct-0.02f, pct, st.y) -
    smoothstep(pct, pct+0.02f, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    y = exponentialEasing(st.x, 0.3);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}
  • 椭圆造型函数:圆弧提供了一种快速且易于编码的方法来缓入或缓出单位正方形。然而,由于使用平方根,该函数的计算效率会降低。
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

float doubleCircleSigmoid (float x, float a){
    float min_param_a = 0.0;
    float max_param_a = 1.0;
    a = max(min_param_a, min(max_param_a, a));

    float y = 0.0;
    if (x<=a){
        y = a - sqrt(a*a - x*x);
    } else {
        y = a + sqrt(pow((1.0-a), 2.0) - pow((x-1.0), 2.0));
    }
    return y;
}

float plot(vec2 st, float pct){
    return smoothstep(pct-0.02f, pct, st.y) -
    smoothstep(pct, pct+0.02f, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    y = doubleCircleSigmoid(st.x, 0.3);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}
  • 贝塞尔造型函数:此函数定义一个二阶​​(二次)贝塞尔曲线,该曲线在单位正方形中具有单个用户指定的样条控制点(位于坐标 a,b)。此函数保证具有与双线性插值器相同的进入和退出斜率。换句话说,此曲线允许用户精确指定其在单位正方形中端点的变化率。
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

float quadraticBezier (float x, float a, float b){
    // adapted from BEZMATH.PS (1993)
    // by Don Lancaster, SYNERGETICS Inc.
    // http://www.tinaja.com/text/bezmath.html

    float epsilon = 0.00001;
    a = max(0.0, min(1.0, a));
    b = max(0.0, min(1.0, b));
    if (a == 0.5){
        a += epsilon;
    }

    // solve t from x (an inverse operation)
    float om2a = 1.0 - 2.0*a;
    float t = (sqrt(a*a + om2a*x) - a)/om2a;
    float y = (1.0-2.0*b)*(t*t) + (2.0*b)*t;
    return y;
}

float plot(vec2 st, float pct){
    return smoothstep(pct-0.02f, pct, st.y) -
    smoothstep(pct, pct+0.02f, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    y = quadraticBezier(st.x, 0.001, 5.0);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}
  •  Impulse造型函数:
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

//  Function from Iñigo Quiles
//  www.iquilezles.org/www/articles/functions/functions.htm
float impulse( float k, float x ){
    float h = k*x;
    return h*exp(1.0-h);
}

float plot(vec2 st, float pct){
    return  smoothstep( pct-0.02, pct, st.y) -
    smoothstep( pct, pct+0.02, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    y = impulse(12.0,x);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}
  • CubicPulse造型函数:
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

//  Function from Iñigo Quiles
//  www.iquilezles.org/www/articles/functions/functions.htm
float cubicPulse( float c, float w, float x ){
    x = abs(x - c);
    if( x>w ) return 0.0;
    x /= w;
    return 1.0 - x*x*(3.0-2.0*x);
}

float plot(vec2 st, float pct){
    return  smoothstep( pct-0.02, pct, st.y) -
    smoothstep( pct, pct+0.02, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    y =  cubicPulse(0.5,0.2,st.x);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}
  • ExpStep造型函数:
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

//  Function from Iñigo Quiles
//  www.iquilezles.org/www/articles/functions/functions.htm
float expStep(float x, float k, float n){
    return exp(-k*pow(x, n));
}


float plot(vec2 st, float pct){
    return smoothstep(pct-0.02, pct, st.y) -
    smoothstep(pct, pct+0.02, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    y =  expStep(st.x, 10., 1.0);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}
  • Parabola造型函数:
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

//  Function from Iñigo Quiles
//  www.iquilezles.org/www/articles/functions/functions.htm
float parabola(float x, float k){
    return pow(4.0*x*(1.0-x), k);
}


float plot(vec2 st, float pct){
    return smoothstep(pct-0.02, pct, st.y) -
    smoothstep(pct, pct+0.02, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    y  = parabola(st.x, 1.0);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}
  • Pcurve造型函数:
#version 320 es
precision mediump float;

#define PI 3.14159265359;

out vec4 FragColor;

uniform vec2 u_resolution;

//  Function from Iñigo Quiles
//  www.iquilezles.org/www/articles/functions/functions.htm
float pcurve(float x, float a, float b){
    float k = pow(a+b, a+b) / (pow(a, a)*pow(b, b));
    return k * pow(x, a) * pow(1.0-x, b);
}


float plot(vec2 st, float pct){
    return smoothstep(pct-0.02, pct, st.y) -
    smoothstep(pct, pct+0.02, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    float y;
    float x = st.x;

    y  = pcurve(st.x, 3.0, 1.0);
    // Plot a line
    float pct = plot(st, y);

    vec3 color = pct * vec3(1.0f, 0.0f, 0.0f);

    FragColor = vec4(color, 1.0f);
}

三、Shader的颜色:

        GLSL中向量类型的另一大特点是可以用你需要的任意顺序简单地投射和混合(变量)值。这种能力被(形象地)称为:鸡尾酒

        混合颜色:

        在GLSL中,有个十分有用的函数:mix() 这个函数让你以百分比混合两个值。

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量

vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);

void main() {
    vec3 color = vec3(0.0);
    float pct = abs(sin(u_time));
    // Mix uses pct (a value from 0-1) to
    // mix the two colors
    color = mix(colorA, colorB, pct);

    FragColor = vec4(color, 1.0f);
}

        缓动函数:

        Robert Penner 开发了一些列流行的计算机动画塑形函数,被称为缓动函数。

#version 320 es
precision mediump float;

#define PI 3.141592653589793f;
#define HALF_PI 1.5707963267948966f;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


// Robert Penner's easing functions in GLSL
// https://github.com/stackgl/glsl-easings
float linear(float t) {
    return t;
}

float exponentialIn(float t) {
    return t == 0.0 ? t : pow(2.0, 10.0 * (t - 1.0));
}

float exponentialOut(float t) {
    return t == 1.0 ? t : 1.0 - pow(2.0, -10.0 * t);
}

float exponentialInOut(float t) {
    return t == 0.0 || t == 1.0
    ? t
    : t < 0.5
    ? +0.5 * pow(2.0, (20.0 * t) - 10.0)
    : -0.5 * pow(2.0, 10.0 - (t * 20.0)) + 1.0;
}

float sineIn(float t) {
    float result = (t - 1.0) * HALF_PI;
    return sin(result) + 1.0f;
}

float sineOut(float t) {
    float result = t * HALF_PI;
    return sin(result);
}

float qinticIn(float t) {
    return pow(t, 5.0);
}

float qinticOut(float t) {
    return 1.0 - (pow(t - 1.0, 5.0));
}

float qinticInOut(float t) {
    return t < 0.5
    ? +16.0 * pow(t, 5.0)
    : -0.5 * pow(2.0 * t - 2.0, 5.0) + 1.0;
}

float quarticIn(float t) {
    return pow(t, 4.0);
}

float quarticOut(float t) {
    return pow(t - 1.0, 3.0) * (1.0 - t) + 1.0;
}

float quarticInOut(float t) {
    return t < 0.5
    ? +8.0 * pow(t, 4.0)
    : -8.0 * pow(t - 1.0, 4.0) + 1.0;
}

float quadraticInOut(float t) {
    float p = 2.0 * t * t;
    return t < 0.5 ? p : -p + (4.0 * t) - 1.0;
}

float quadraticIn(float t) {
    return t * t;
}

float quadraticOut(float t) {
    return -t * (t - 2.0);
}

float cubicIn(float t) {
    return t * t * t;
}

float cubicOut(float t) {
    float f = t - 1.0;
    return f * f * f + 1.0;
}

float cubicInOut(float t) {
    return t < 0.5
    ? 4.0 * t * t * t
    : 0.5 * pow(2.0 * t - 2.0, 3.0) + 1.0;
}

float elasticIn(float t) {
    float result = 13.0 * t * HALF_PI;

    return sin(result) * pow(2.0, 10.0 * (t - 1.0));
}

float elasticOut(float t) {
    float result = -13.0 * (t + 1.0) * HALF_PI;
    return sin(result) * pow(2.0, -10.0 * t) + 1.0;
}

float elasticInOut(float t) {
    float result1 = 13.0 * HALF_PI;

    float result2 = -13.0 * HALF_PI;
    return t < 0.5
    ? 0.5 * sin(result1 * 2.0 * t) * pow(2.0, 10.0 * (2.0 * t - 1.0))
    : 0.5 * sin(result2 * ((2.0 * t - 1.0) + 1.0)) * pow(2.0, -10.0 * (2.0 * t - 1.0)) + 1.0;
}

float circularIn(float t) {
    return 1.0 - sqrt(1.0 - t * t);
}

float circularOut(float t) {
    return sqrt((2.0 - t) * t);
}

float circularInOut(float t) {
    return t < 0.5
    ? 0.5 * (1.0 - sqrt(1.0 - 4.0 * t * t))
    : 0.5 * (sqrt((3.0 - 2.0 * t) * (2.0 * t - 1.0)) + 1.0);
}

float bounceOut(float t) {
    const float a = 4.0 / 11.0;
    const float b = 8.0 / 11.0;
    const float c = 9.0 / 10.0;

    const float ca = 4356.0 / 361.0;
    const float cb = 35442.0 / 1805.0;
    const float cc = 16061.0 / 1805.0;

    float t2 = t * t;

    return t < a
    ? 7.5625 * t2
    : t < b
    ? 9.075 * t2 - 9.9 * t + 3.4
    : t < c
    ? ca * t2 - cb * t + cc
    : 10.8 * t * t - 20.52 * t + 10.72;
}

float bounceIn(float t) {
    return 1.0 - bounceOut(1.0 - t);
}

float bounceInOut(float t) {
    return t < 0.5
    ? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0))
    : 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5;
}

float backIn(float t) {
    float result = t * PI;
    return pow(t, 3.0) - t * sin(result);
}

float backOut(float t) {
    float f = 1.0 - t;
    float result = f * PI;
    return 1.0 - (pow(f, 3.0) - f * sin(result));
}

float backInOut(float t) {
    float f = t < 0.5 ? 2.0 * t : 1.0 - (2.0 * t - 1.0);
    float result = f * PI;
    float g = pow(f, 3.0) - f * sin(result);

    return t < 0.5
    ? 0.5 * g
    : 0.5 * (1.0 - g) + 0.5;
}

void main() {
    vec3 colorA = vec3(0.149, 0.141, 0.912);
    vec3 colorB = vec3(1.000, 0.833, 0.224);

    float t = sin(u_time);
    float pct;
//    pct = cubicInOut(abs(fract(t)*2.0-1.0));
    pct = elasticIn(t);
    vec3 color = mix(colorA, colorB, pct);
    FragColor = vec4(color, 1.0f);
}

        渐变:

        我们可以输入两个互相匹配的变量类型而不仅仅是单独的 float 变量,在我们这个例子中用的是 vec3。这样我们便获得了混合颜色单独通道 .r.g 和 .b的能力。

#version 320 es
precision mediump float;

#define PI 3.141592653589793f;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


vec3 colorA = vec3(0.149, 0.141, 0.912);
vec3 colorB = vec3(1.000, 0.833, 0.224);

float plot (vec2 st, float pct){
    return smoothstep(pct-0.01, pct, st.y) -
    smoothstep(pct, pct+0.01, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    vec3 pct = vec3(st.x);

    pct.r = smoothstep(0.0, 1.0, st.x);
    float result = st.x * PI;
    pct.g = sin(result);
    pct.b = pow(st.x, 0.5);

    color = mix(colorA, colorB, pct);

    // Plot transition lines for each channel
    color = mix(color, vec3(1.0, 0.0, 0.0), plot(st, pct.r));
    color = mix(color, vec3(0.0, 1.0, 0.0), plot(st, pct.g));
    color = mix(color, vec3(0.0, 0.0, 1.0), plot(st, pct.b));

    FragColor = vec4(color, 1.0f);
}

        HSB:

        不能脱离色彩空间来谈论颜色。正如所知,除了rgb值,有其他不同的方法去描述定义颜色。

        HSB代表色相,饱和度和亮度(或称为值)。这更符合直觉也更有利于组织颜色。将x坐标(位置)映射到Hue值并将y坐标映射到明度,我们就得到了五彩的可见光光谱。这样的色彩空间分布实现起来非常方便,比起RGB,用HSB来拾取颜色更直观。

  • H(Hue):表示颜色的类型(例如红色,绿色或者黄色).取值范围为0—360.其中每一个值代表一种颜色.
  • S(Saturation):颜色的饱和度从0到1有时候也称为纯度.(0表示灰度图,1表示纯的颜色)
  • B(Brightness or Value):颜色的明亮程度从0到1(0表示黑色,1表示特定饱和度的颜色)

        下面是rgb2hsv() 和 hsv2rgb() 函数的例子:

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


vec3 rgb2hsb(in vec3 c){
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz),
    vec4(c.gb, K.xy),
    step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r),
    vec4(c.r, p.yzx),
    step(p.x, c.r));
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
    d / (q.x + e),
    q.x);
}

//  Function from Iñigo Quiles
//  https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb(in vec3 c){
    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0, 4.0, 2.0),
    6.0)-3.0)-1.0,
    0.0,
    1.0);
    rgb = rgb*rgb*(3.0-2.0*rgb);
    return c.z * mix(vec3(1.0), rgb, c.y);
}


void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);
    color = hsb2rgb(vec3(st.x, 1.0, st.y));

    FragColor = vec4(color, 1.0f);
}

        

四、形状:

        正方形:

        下面是一个画一个正方形的代码,思路为:step()函数会让每一个小于0.1的像素变成黑色(vec(0.0)并将其余的变成白色(vec3(1.0))。left 乘 bottom 效果相当于逻辑 AND —— 当 x y 都为 1.0 时乘积才能是 1.0。这样做的效果就是画了两条黑线,一个在画布的底边另一个在左边。同理top和right也是这样。

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量



void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    vec3 color = vec3(0.0);

    // bottom-left
    vec2 bl = step(vec2(0.1), st);
    float pct = bl.x * bl.y;

    // top-right
    vec2 tr = step(vec2(0.1), 1.0-st);
    pct *= tr.x * tr.y;

    color = vec3(pct);

    FragColor = vec4(color, 1.0f);
}

        圆:

        圆的实现有几种方法:

  • 像素到中心的距离:
  • 从像素到中心的矢量长度;
  • 从像素到中心的向量的平方根;

        distance()这个函数其实内部调用length()函数,计算不同两点的距离(在此例中是像素坐标和画布中心的距离)。length()函数内部只不过是用平方根sqrt()计算斜边的方程。

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    vec3 color = vec3(0.0);

    // bottom-left
    vec2 bl = step(vec2(0.1), st);
    float pct = bl.x * bl.y;

    // top-right
    vec2 tr = step(vec2(0.1), 1.0-st);
    pct *= tr.x * tr.y;

    color = vec3(pct);

    FragColor = vec4(color, 1.0f);
}

        距离场:

        我们可也可以从另外的角度思考上面的例子:把它当做海拔地图(等高线图)——越黑的地方意味着海拔越高。想象下,你就在圆锥的顶端,那么这里的渐变就和圆锥的等高线图有些相似。到圆锥的水平距离是一个常数0.5。这个距离值在每个方向上都是相等的。通过选择从那里截取这个圆锥,你就会得到或大或小的圆纹面。

        其实我们是通过“空间距离”来重新解释什么是图形。这种技巧被称之为“距离场”,从字体轮廓到3D图形被广泛应用。

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


float circle(in vec2 _st, in float _radius){
    vec2 dist = _st-vec2(0.5, 0.3);
    return 1.-smoothstep(_radius-(_radius*0.01),
    _radius+(_radius*0.01),
    dot(dist, dist)*5.0);
}

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    //获取屏幕分辨率的长宽比
    float rateXY = u_resolution.x / u_resolution.y;

    vec2 st1 = vec2(st.x * rateXY, st.y);

    vec3 color = vec3(circle(st1, 0.2));

    FragColor = vec4(color, 1.0f);
}

        距离场景2:

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;

    st.x *= u_resolution.x/u_resolution.y;
    vec3 color = vec3(0.0);
    float d = 0.0;

    // Remap the space to -1. to 1.
    st = st *2.0-1.0;

    // Make the distance field
    d = length(abs(st)-0.3);
    // d = length( min(abs(st)-.3,0.) );
    // d = length( max(abs(st)-.3,0.) );

    // Visualize the distance field
    color = vec3(fract(d*10.0));

    // Drawing with the distance field
    //    color = vec3(step(0.3, d));
    //    color = vec3(step(0.3,d) * step(d,0.4));

    FragColor = vec4(color, 1.0f);
}

        极坐标图形:

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    vec2 pos = vec2(0.5)-st;

    float r = length(pos)*2.0;
    float a = atan(pos.y, pos.x);

    float f = cos(a*3.0);
    // f = abs(cos(a*3.));
    // f = abs(cos(a*2.5))*.5+.3;
    // f = abs(cos(a*12.)*sin(a*3.))*.8+.1;
    // f = smoothstep(-.5,1., cos(a*10.))*0.2+0.5;

    color = vec3(1.0-smoothstep(f, f+0.02, r));

    // Drawing with the distance field
    //    color = vec3(step(0.3, d));
    //    color = vec3(step(0.3,d) * step(d,0.4));

    FragColor = vec4(color, 1.0f);
}

五、Matrices(二维矩阵):

        如上图形 - 而如何移动它们的技巧则是借助移动它们自身的参考坐标系。我们只需要给 st 变量加上一个包含每个片段的位置的向量。这样就移动了整个坐标系。

        平移:

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


float box(in vec2 _st, in vec2 _size){
    _size = vec2(0.5) - _size*0.5;
    vec2 uv = smoothstep(_size,
    _size+vec2(0.001),
    _st);

    uv *= smoothstep(_size,
    _size+vec2(0.001),
    vec2(1.0)-_st);

    return uv.x*uv.y;
}

float crossT(in vec2 _st, float _size){
    return box(_st, vec2(_size, _size/4.0)) +
    box(_st, vec2(_size/4.0, _size));
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    vec3 color = vec3(0.0);

    // 为了移动十字架,我们移动了空间
    vec2 translate = vec2(cos(u_time), sin(u_time));
    st += translate*0.35;

    // 在背景上显示空间的坐标
     color = vec3(st.x,st.y,0.0);

    // 在视窗上添加形状
    color += vec3(crossT(st, 0.15));

    FragColor = vec4(color, 1.0f);
}

        矩阵:

        要移动物体,我们同样需要移动整个空间(坐标)系统。为此我们将使用一个矩阵。矩阵是一个通过行和列定义的一组数。用矩阵乘以一个向量是用一组精确的规则定义的,这样做是为了以一组特定的方式来改变向量的值。

        

                 GLSL本身支持2维,3维和4维方阵(mm矩阵):mat2、mat3、mat4。GLSL同样支持矩阵相乘 (```)和特殊矩阵函数([matrixCompMult()```](../glossary/?search=matrixCompMult))。

  • 矩阵平移:基于矩阵的特性,我们便有可能构造一个矩阵来产生特定的作用。比如我们可以用一个矩阵来平移一个向量。

        

  •  矩阵旋转: 可以用矩阵来旋转坐标系统。

        

        下面构成2维旋转的矩阵的代码,将二维向量绕 vec2(0.0) 点旋转。

mat2 rotate2d(float _angle){
    return mat2(cos(_angle),-sin(_angle),
                sin(_angle),cos(_angle));
}
  •  矩阵缩放:如果你用过3D建模软件或者 Processing中的 pushmatrix 和 popmatrix 函数,你会知道矩阵也可以被用来缩放物体的大小。

        

           根据上面的公式,我们知道如何构造一个2D缩放矩阵:

mat2 scale(vec2 _scale){
    return mat2(_scale.x,0.0,
                0.0,_scale.y);
}

        矩阵旋转:

#version 320 es
precision mediump float;
#define PI 3.14159265359;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


mat2 rotate2d(float _angle){
    return mat2(cos(_angle), -sin(_angle),
    sin(_angle), cos(_angle));
}

float box(in vec2 _st, in vec2 _size){
    _size = vec2(0.5) - _size*0.5;
    vec2 uv = smoothstep(_size,
    _size+vec2(0.001),
    _st);
    uv *= smoothstep(_size,
    _size+vec2(0.001),
    vec2(1.0)-_st);
    return uv.x*uv.y;
}

float crossR(in vec2 _st, float _size){
    return box(_st, vec2(_size, _size/4.)) +
    box(_st, vec2(_size/4., _size));
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    vec3 color = vec3(0.0);

    // 将空间从中心移动到vec2(0.0)
    st -= vec2(0.5);
    // 旋转空间
    float angle = sin(u_time)*PI;
    st = rotate2d(angle) * st;
    // 将其移回原位
    st += vec2(0.5);

    // 在背景上显示空间的坐标
     color = vec3(st.x,st.y,0.0);

    // 在视窗上添加形状
    color += vec3(crossR(st, 0.4));

    FragColor = vec4(color, 1.0f);
}

        矩阵缩放:

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


mat2 scale(vec2 _scale){
    return mat2(_scale.x, 0.0,
    0.0, _scale.y);
}

float box(in vec2 _st, in vec2 _size){
    _size = vec2(0.5) - _size*0.5;
    vec2 uv = smoothstep(_size,
    _size+vec2(0.001),
    _st);
    uv *= smoothstep(_size,
    _size+vec2(0.001),
    vec2(1.0)-_st);
    return uv.x*uv.y;
}

float crossS(in vec2 _st, float _size){
    return box(_st, vec2(_size, _size/4.0)) +
    box(_st, vec2(_size/4.0, _size));
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    vec3 color = vec3(0.0);

    st -= vec2(0.5);
    st = scale(vec2(sin(u_time)+1.0)) * st;
    st += vec2(0.5);

    // 在背景上显示空间的坐标
    color = vec3(st.x, st.y, 0.0);

    // 在视窗上添加形状
    color += vec3(crossS(st, 0.2));

    FragColor = vec4(color, 1.0f);
}

        YUV三维矩阵转换:

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


// YUV to RGB matrix
mat3 yuv2rgb = mat3(1.0, 0.0, 1.13983,
1.0, -0.39465, -0.58060,
1.0, 2.03211, 0.0);

// RGB to YUV matrix
mat3 rgb2yuv = mat3(0.2126, 0.7152, 0.0722,
-0.09991, -0.33609, 0.43600,
0.615, -0.5586, -0.05639);



void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    vec3 color = vec3(0.0);

    // UV值从-1到1
    // 因此,我们需要重新映射st(0.0到1.0)
    st -= 0.5;// becomes -0.5 to 0.5
    st *= 2.0;// becomes -1.0 to 1.0

    // 我们将st作为三维向量的y&z值传递给正确乘以3x3矩阵
    color = yuv2rgb * vec3(0.5, st.x, st.y);

    FragColor = vec4(color, 1.0f);
}

       

六、图案:

        我们的策略依然基于乘以空间坐标(0到1之间),这样我们的画的在0到1之间的图形就会重复地形成网格。

        图案内部应用标量重复:

        fract()函数,它返回一个数的分数部分,本质上是除1的余数。换句话说fract()返回小数点后的数。 我们单位化的坐标系变量 (st) 已经是 0.0 到 1.0 之间的了。

#version 320 es
precision mediump float;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


float circle(in vec2 _st, in float _radius){
    vec2 l = _st-vec2(0.5);
    return 1.0-smoothstep(_radius-(_radius*0.01),
    _radius+(_radius*0.01),
    dot(l, l)*4.0);
}
void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    st.x *= u_resolution.x / u_resolution.y;
    
    vec3 color = vec3(0.0);

    st *= 5.0;// Scale up the space by 3
    st = fract(st);// Wrap around 1.0

    // Now we have 9 spaces that go from 0-1

    color = vec3(st, 0.0);
    color = vec3(circle(st, 0.5));

    FragColor = vec4(color, 1.0f);
}

图案内部应用矩阵:

#version 320 es
precision mediump float;
#define PI 3.14159265358979323846f;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


vec2 rotate2D(vec2 _st, float _angle){
    _st -= 0.5;
    _st =  mat2(cos(_angle), -sin(_angle),
    sin(_angle), cos(_angle)) * _st;
    _st += 0.5;
    return _st;
}

vec2 tile(vec2 _st, float _zoom){
    _st *= _zoom;
    return fract(_st);
}

float box(vec2 _st, vec2 _size, float _smoothEdges){
    _size = vec2(0.5)-_size*0.5;
    vec2 aa = vec2(_smoothEdges*0.5);
    vec2 uv = smoothstep(_size, _size+aa, _st);
    uv *= smoothstep(_size, _size+aa, vec2(1.0)-_st);
    return uv.x*uv.y;
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    st.x *= u_resolution.x / u_resolution.y;
    vec3 color = vec3(0.0);
    // Divide the space in 4
    st = tile(st, 4.0);

    // Use a matrix to rotate the space 45 degrees
    float angle = 0.25f * PI;
    st = rotate2D(st, angle);

    // Draw a square
    color = vec3(box(st, vec2(0.7), 0.01));
    // color = vec3(st,0.0);

    FragColor = vec4(color, 1.0f);
}

        图案偏移:

#version 320 es
precision mediump float;
#define PI 3.14159265358979323846f;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


vec2 brickTile(vec2 _st, float _zoom){
    _st *= _zoom;

    // Here is where the offset is happening
    _st.x += step(1., mod(_st.y,2.0)) * 0.5;

    return fract(_st);
}

float box(vec2 _st, vec2 _size){
    _size = vec2(0.5)-_size*0.5;
    vec2 uv = smoothstep(_size,_size+vec2(1e-4),_st);
    uv *= smoothstep(_size,_size+vec2(1e-4),vec2(1.0)-_st);
    return uv.x*uv.y;
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    st.x *= u_resolution.x / u_resolution.y;
    vec3 color = vec3(0.0);

    // Modern metric brick of 215mm x 102.5mm x 65mm
    // http://www.jaharrison.me.uk/Brickwork/Sizes.html
    // st /= vec2(2.15,0.65)/1.5;

    // Apply the brick tiling
    st = brickTile(st,5.0);

    color = vec3(box(st,vec2(0.9)));

    // Uncomment to see the space coordinates
    color = vec3(st,0.0);

    FragColor = vec4(color, 1.0f);
}

        Truchet瓷砖:

        目前我们学了如何区分奇数行/列或偶数行/列,(类似的),(我们也)可能再用(这个技巧)根据位置来设计元素。Truchet瓷砖,即一个单一设计元素可以以四种不同的方式呈现,仔细观察 rotateTilePattern() 函数, 它把坐标空间细分成四个单元并赋予每一个旋转值。

#version 320 es
precision mediump float;
#define PI 3.14159265358979323846f;

out vec4 FragColor;//out代表输出至下一个着色器

uniform vec2 u_resolution;//视窗的分辨率
uniform float u_time;//传入的时间变量


vec2 rotate2D (vec2 _st, float _angle) {
    _st -= 0.5;
    _st =  mat2(cos(_angle), -sin(_angle),
    sin(_angle), cos(_angle)) * _st;
    _st += 0.5;
    return _st;
}

vec2 tile (vec2 _st, float _zoom) {
    _st *= _zoom;
    return fract(_st);
}

vec2 rotateTilePattern(vec2 _st){

    //  Scale the coordinate system by 2x2
    _st *= 2.0;

    //  Give each cell an index number
    //  according to its position
    float index = 0.0;
    index += step(1., mod(_st.x, 2.0));
    index += step(1., mod(_st.y, 2.0))*2.0;

    //      |
    //  2   |   3
    //      |
    //--------------
    //      |
    //  0   |   1
    //      |

    // Make each cell between 0.0 - 1.0
    _st = fract(_st);

    // Rotate each cell according to the index
    if (index == 1.0){
        //  Rotate cell 1 by 90 degrees
        float angle = 0.5* PI;
        _st = rotate2D(_st, angle);
    } else if (index == 2.0){
        //  Rotate cell 2 by -90 degrees
        float angle =  - 0.5 * PI;
        _st = rotate2D(_st, angle);
    } else if (index == 3.0){
        //  Rotate cell 3 by 180 degrees
        float angle = PI;
        _st = rotate2D(_st, angle);
    }

    return _st;
}


void main() {
    vec2 st = gl_FragCoord.xy / u_resolution;
    st.x *= u_resolution.x / u_resolution.y;
    vec3 color = vec3(0.0);

    st = tile(st, 3.0);
    st = rotateTilePattern(st);

    // Make more interesting combinations
    //        st = tile(st, 2.0);
    float angle2D1 = - u_time *0.25 *PI;
    st = rotate2D(st, angle2D1);
    st = rotateTilePattern(st*2.0);
    float angle2D2 = u_time*0.25 * PI;
    st = rotate2D(st, angle2D2);

    // step(st.x,st.y) just makes a b&w triangles
    // but you can use whatever design you want.
    // Uncomment to see the space coordinates
    color = vec3(step(st.x, st.y));

    FragColor = vec4(color, 1.0f);
}

       

参考资料:

        The Book of Shader

        thebookofshader的github地址:thebookofshader

        GitHub - McNopper/OpenGL: OpenGL 3 and 4 with GLSL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/980627.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于 Flink CDC YAML 的 MySQL 到 Kafka 流式数据集成

本教程的演示都将在 Flink CDC CLI 中进行&#xff0c;无需一行 Java/Scala 代码&#xff0c;也无需安装 IDE。 这篇教程将展示如何基于 Flink CDC YAML 快速构建 MySQL 到 Kafka 的 Streaming ELT 作业&#xff0c;包含整库同步、表结构变更同步演示和关键参数介绍。 准备阶段…

【Maven】基于IDEA进行Maven工程的创建、构建

文章目录 一、基于IDEA创建Maven工程1. 概念梳理Maven工程的GAVP2. Idea构建Maven Java SE工程3. Idea构建Maven Java Web工程3.1 创建一个maven的javase工程3.2 修改pom.xml文件打包方式3.3 设置web资源路径和web.xml路径 4. Maven工程项目结构说明 二、基于IDEA进行Maven工程…

计算机毕业设计SpringBoot+Vue.js在线课程管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

【爬虫基础】第二部分 爬虫基础理论 P3/3

上节内容回顾&#xff1a;【爬虫基础】第一部分 网络通讯 P1/3-CSDN博客 【爬虫基础】第一部分 网络通讯-Socket套接字 P2/3-CSDN博客 【爬虫基础】第一部分 网络通讯-编程 P3/3-CSDN博客 【爬虫基础】第二部分 爬虫基础理论 P1/3-CSDN博客 【爬虫基础】第二部分 爬虫基础理论…

【子网掩码计算器:Python + Tkinter 实现】

子网掩码计算器&#xff1a;Python Tkinter 实现 引言代码功能概述代码实现思路1. 界面设计2. 功能实现3. 事件处理 子网掩码计算器实现步骤1. 导入必要的库2. 定义主窗口类 SubnetCalculatorApp3. 创建菜单栏4. 创建界面组件5. 判断 IP 地址类别6. 计算子网信息7. 其他功能函…

【第十节】C++设计模式(结构型模式)-Flyweight( 享元)模式

目录 一、问题背景 二、模式选择 三、代码实现 四、总结讨论 一、问题背景 享元模式&#xff08;Flyweight Pattern&#xff09;在对象存储优化中的应用 在面向对象系统的设计与实现中&#xff0c;创建对象是最常见的操作之一。然而&#xff0c;如果一个应用程序使用了过多…

macOS - 使用 tmux

文章目录 安装 tmux使用更多快捷键说明 安装 tmux brew install tmux使用 在终端输入 tmux 进入 tmux 界面&#xff0c;然后 输入 Control Option B 进入交互模式 输入 % 左右分栏&#xff0c;" 上下分割 上一个窗格&#xff1a;{&#xff0c;下一个&#xff1a;} PS…

【洛谷贪心算法题】P1094纪念品分组

该题运用贪心算法&#xff0c;核心思想是在每次分组时&#xff0c;尽可能让价格较小和较大的纪念品组合在一起&#xff0c;以达到最少分组的目的。 【算法思路】 输入处理&#xff1a;首先读取纪念品的数量n和价格上限w&#xff0c;然后依次读取每件纪念品的价格&#xff0c;…

16. LangChain实战项目2——易速鲜花内部问答系统

需求简介 易束鲜花企业内部知识库如下&#xff1a; 本实战项目设计一个内部问答系统&#xff0c;基于这些内部知识&#xff0c;回答内部员工的提问。 在前面课程的基础上&#xff0c;需要安装的依赖包如下&#xff1a; pip install docx2txt pip install qdrant-client pip i…

Minio搭建并在SpringBoot中使用完成用户头像的上传

Minio使用搭建并上传用户头像到服务器操作,学习笔记 Minio介绍 minio官网 MinIO是一个开源的分布式对象存储服务器&#xff0c;支持S3协议并且可以在多节点上实现数据的高可用和容错。它采用Go语言开发&#xff0c;拥有轻量级、高性能、易部署等特点&#xff0c;并且可以自由…

Spring AI:让AI应用开发更简单

文章目录 引言什么是Spring AI&#xff1f;核心特性 Spring AI的核心组件ChatClient&#xff1a;聊天模型示例代码图示 ImageClient&#xff1a;图像生成示例代码图示 Prompt Templates&#xff1a;提示词模板示例代码 Spring AI的优势示例项目&#xff1a;智能机票助手代码实现…

【C】链式二叉树算法题1 -- 单值二叉树

leetcode链接https://leetcode.cn/problems/univalued-binary-tree/description/ 1 题目描述 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false。 示例 1&#xff1…

什么是最终一致性,它对后端系统的意义是什么

最终一致性(Eventual Consistency)是分布式系统中的一种一致性模型。与传统的强一致性模型不同,最终一致性并不要求系统在任何时刻都保持一致,而是保证在足够的时间后,所有节点的数据最终会达到一致的状态。换句话说,系统允许短时间内出现数据的不一致性,但最终会通过某…

掌握大模型高效任务流搭建(一):构建LangChain任务流

前言&#xff1a; 在LangChain框架中&#xff0c;“链”占据着核心地位。它允许我们将众多任务模块串联起来&#xff0c;构建出富有弹性的任务流。借助这种链式结构&#xff0c;我们能够处理复杂的逻辑&#xff0c;并实现任务的自动化。在实际场景里&#xff0c;链式操作极大地…

目标检测——数据处理

1. Mosaic 数据增强 Mosaic 数据增强步骤: (1). 选择四个图像&#xff1a; 从数据集中随机选择四张图像。这四张图像是用来组合成一个新图像的基础。 (2) 确定拼接位置&#xff1a; 设计一个新的画布(输入size的2倍)&#xff0c;在指定范围内找出一个随机点&#xff08;如…

塑造网络安全的关键事件

注&#xff1a;本文为 “网络安全” 相关文章合辑。 机翻&#xff0c;未校。 Timeline of Cyber Security: Key Events that Shaped the Field 网络安全时间表&#xff1a;塑造该领域的关键事件 October 29, 2023 Cyberattacks are an everyday threat, always changing. T…

题解 | 牛客周赛82 Java ABCDEF

目录 题目地址 做题情况 A 题 B 题 C 题 D 题 E 题 F 题 牛客竞赛主页 题目地址 牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 做题情况 A 题 判断字符串第一个字符和第三个字符是否相等 import java.io.*; import java.math.*; import java.u…

Redis 高可用性:如何让你的缓存一直在线,稳定运行?

&#x1f3af; 引言&#xff1a;Redis的高可用性为啥这么重要&#xff1f; 在现代高可用系统中&#xff0c;Redis 是一款不可或缺的分布式缓存与数据库系统。无论是提升访问速度&#xff0c;还是实现数据的高效持久化&#xff0c;Redis 都能轻松搞定。可是&#xff0c;当你把 …

uniapp-原生android插件开发摘要

uni-app在App侧的原生扩展插件&#xff0c;支持使用java、object-c等原生语言编写&#xff0c;从HBuilderX 3.6起&#xff0c;新增支持了使用uts来开发原生插件。 基础项目 UniPlugin-Hello-AS工程请在App离线SDK中查找 基础项目(App离线SDK)已经配置好了自定义插件所需要的…

【定昌Linux系统】部署了java程序,设置开启启动

将代码上传到相应的目录&#xff0c;并且配置了一个.sh的启动脚本文件 文件内容&#xff1a; #!/bin/bash# 指定JAR文件的路径&#xff08;如果JAR文件在当前目录&#xff0c;可以直接使用文件名&#xff09; JAR_FILE"/usr/local/java/xs_luruan_client/lib/xs_luruan_…