ShaderToy入门初体验

2023-11-01

ShaderToy入门初体验

前言

   [shadertoy](https://www.shadertoy.com/)
   那些大神写的demo简直太震撼了。

官方给出的how to在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

第一个程序

在这里插入图片描述
点击上图中的“新建”,弹出下图界面
在这里插入图片描述

代码详解

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;

    // Time varying pixel color
    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));

    // Output to screen
    fragColor = vec4(col,1.0);
}

这是ShaderToy的主函数mainImage,
第一个参数出:out vec4 fragColor,输出的是像素的颜色向量。
第二个参数入in: vec2 fragCoord,输入的像素坐标向量,主要作用就是根据屏幕上的像素坐标,算出像素的颜色向量,简单来说完成像素坐标到颜色的变换或者是映射。屏幕分辨率是800乘600的话,就计算800乘600区域内的所有像素。ShaderToy当前窗口的每个像素坐标都要经过这个主函数的处理以决定其颜色,所以看似这个主程序是一段代码,其实逻辑上被嵌在了一个像素坐标的大循环里面。

注意!!!
shadertoy有很多内置变量,这些我们用户不能重定义
/**
*常量定义
*/
//uniform vec3 iResolution; // 窗口分辨率,单位像素
//uniform float iTime; // 程序运行的时间,单位秒
//uniform float iTimeDelta; // 渲染时间,单位秒
//uniform float iFrame; // 帧率
//uniform vec4 iMouse; // 鼠标位置
//uniform vec4 iDate; // 日期(年,月,日,时)
//uniform sampler2D iChannel0 //获取纹理像素颜色
//uniform float iChannelTime[4];
//uniform samplerXX iChanneli;
//uniform SampleRate; //指定的采样率进行采样,根据应用程序,该采样率通常为44100或48000。用于声音着色器
vec2 mainSound( float time )
通过mainSound函数的返回值,将期望的波幅输出为立体声(左和右声道)声音的一对值。
由mainSound入口点生成的着色器将被自动标记为声音着色器,并且可以通过声音输出限定词的过滤器系统进行搜索。

vec2 uv = fragCoord/iResolution.xy;```
uv这里做了归一化处理, uv.x, uv.y的取值都在01;

像素颜色按时间步进变化
vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));

小知识:shadertoy里面计算距离用到SDF
有符号距离场(Signed Distance Field,SDF)
SDF的用途很多,可以用来做large scale AO, 软阴影等。UE4就用SDF来做AO和软阴影,SDF Ray-traced shaow 比PCSS软,而且和CSM相比,因为没有那么大的几何填充负担,所以反而要便宜很多,和静态的Shadow map相比,又可以支持物体级别的移动(虽然不支持顶点动画),所以是一个相当不错的阴影解决方案。堡垒之夜就用了这项技术,堡垒之夜的近处是级联阴影,远处是SDF Ray-traced shadow.

符号距离函数Signed Distance Function是某度量空间X中的一个集合ΩΩ的函数,决定X中任一点到 ΩΩ边界∂Ω∂Ω的距离,并且由x是在ΩΩ 内还是ΩΩ外确定其SDF的正负号:当x在ΩΩ内时,SDF为正;当x在ΩΩ外时,SDF为负。假设d是空间X的一种度量,那么SDF用数学公式表达:
f(x)={d(x,∂Ω)−d(x,∂Ω)if x∈Ωif x∈Ωc
f(x)={d(x,∂Ω)if x∈Ω−d(x,∂Ω)if x∈Ωc
这种函数可以用来表示曲线:f(x)=0f(x)=0。

光线步进——RayMarching

RayMarching 是一种用于实时场景的快速渲染方法,我的理解是,模拟摄像机位置,根据视椎体的扩张角度,以摄像机位置为原点,进步式发射射线,当射线碰撞到物体之后,返回其深度信息,如果到视椎体的最大距离之前都没有返回,那么可以以此判断该像素点没有对于物体,最后根据返回的信息计算光照。
可以看出,RayMarching是有误差的,如果提高精度,减少步长,循环次数又太多,导致效率很低。
感觉RayMarching目前用来渲染云,雾这些类似的体积渲染比较多。

创建一个camera

我们需要去定义camera的origin,target,和up 就是定义摄像机的起源,目标位置,还有就是定义向上的位置。

vec3 cameraOrigin = vec3(2.0, 3.0, 2.0);
vec3 cameraTarget = vec3(0.0, 0.0, 0.0);
vec3 upDirection = vec3(0.0, 1.0, 0.0);

然后就可以得出摄像机的方向是:
两个向量相减的物理意义:是得到方向

vec3 cameraDir = normalize(cameraTarget - cameraOrigin);

由此可以计算出摄像机的右方向和顶上的方向。
叉乘 cross(a,b)的物理意义:得到a和b的法线

vec3 cameraRight = normalize(cross(upDirection, cameraOrigin));
vec3 cameraUp = cross(cameraDir, cameraRight);

下面对屏幕坐标进行转化,把屏幕坐标放缩到-1到1之间。

vec2 screenPos = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy; // screenPos can range from -1 to 1
screenPos.x *= iResolution.x / iResolution.y; // Correct aspect ratio

在知道了摄像机的方向之后,我们来计算出ray的方向。

vec3 rayDir = normalize(cameraRight * screenPos.x + cameraUp * screenPos.y + cameraDir);

Raymarching loop

在 marching 里面 先来设置步进的光线总长度

const int MAX_ITER = 100; 

物体的离摄像机的最大范围

const float MAX_DIST = 20.0;

设置物体离光线的阈值距离

const float EPSILON = 0.001;

下面是loop的代码:在个里面点会被转化为和交集的东西。

 // The raymarching loop
float totalDist = 0.0;
vec3 pos = cameraOrigin;
float dist = EPSILON;

// trying to find a point of intersection 
for (int i = 0; i < MAX_ITER; i++)
{
    // Either we've hit the object or hit nothing at all, either way we should break out of the loop
    if (dist < EPSILON || totalDist > MAX_DIST)
    break; // If you use windows and the shader isn't working properly, change this to continue;

    dist = distfunc(pos); // Evalulate the distance at the current point
    totalDist += dist;
    pos += dist * rayDir; // Advance the point forwards in the ray direction by the distance
}

定义显示的模型

float sphere(vec3 pos, float radius)
{
	return length(pos) - radius;
}

float box(vec3 pos, vec3 size)
{
    return length(max(abs(pos) - size, 0.0));
}

定义一下Lighting
光也要在EPSILON的距离里面

if (dist < EPSILON)
{
    // Lighting code
}
else
{
	gl_FragColor = vec4(0.0);
}

光函数里面需要取得表面着色器的normal向量,可以用点来预计算出点的位置。

vec2 eps = vec2(0.0, EPSILON);
vec3 normal = normalize(vec3(
	distfunc(pos + eps.yxx) - distfunc(pos - eps.yxx),
	distfunc(pos + eps.xyx) - distfunc(pos - eps.xyx),
	distfunc(pos + eps.xxy) - distfunc(pos - eps.xxy)));

由光照公式可以得出

  fragColor = vec4(ambientColor +
                      lambertian * diffuseColor +
                      specular * specColor, 1.0);

原理

在这里插入图片描述

从摄像机位置向屏幕每一个像素点发射一条光线,光线按照一定步长前进,并检测当前光线是否位于物体表面,据此调整光线前进幅度,直到抵达物体表面,再按照一般光线追踪的方法计算颜色值。

步骤

1.定义摄像机的位置,光线的方向;

2.定义场景:场景包含一个球体和平面;

3.定义灯光的方向;

4.计算法线的方向;
// 三维坐标:左手坐标系,X轴指向屏幕右侧,Y轴指向屏幕上面,Z轴垂直于屏幕向里

现在开始,我们定义摄像机的位置为 x = 0, y = 1, z = 0;方向是屏幕每个像素点的方向normalize(vec3(uv.x,uv.y,1.));

 // Camera:ro 位置, rd 方向
    vec3 ro = vec3(0,1,0);
    vec3 rd = normalize(vec3(uv.x,uv.y,1.));

我们的场景包含一个球体和一个平面:平面是xz平面,高度 y=0

vec4 s = vec4(0,1,6,1);// 球的位置(s.xyz)和半径(s.w)

我们现在计算光线步进的距离,每一次向前步进一段距离,这个距离是当前点和场景中所有物体的相交的最小距离;如果前进的总距离大于我们要的最大距离或者当前前进的距离小于一个常数,我们认为这个步进的长度没必要继续进行下去,我们就终止循环。

#define Max_Steps 100    // 最大步数
#define Max_Dist 100.	 // 最大距离
#define Surf_Dist 0.01   //

// 获取当前点和场景中是所有物体相交的最小距离
float GetDist(vec3 p)
{
    vec4 s = vec4(0,1,6,1);// 球的位置(s.xyz)和半径(s.w)
    float sphereDist = length(p-s.xyz)-s.w;// P点到球面的距离
    float planeDist  = p.y;// P点到平面的距离,平面是xz平面,高度y = 0;
    
    float d = min(sphereDist,planeDist);
    return d;
}

float RayMarch(vec3 ro, vec3 rd)
{
    float d0 = 0.;
    for(int i = 0; i < Max_Steps; i++)
    {
        vec3 p = ro + rd*d0;
        float ds = GetDist(p);
        d0+=ds;
        if(d0>Max_Dist || ds < Surf_Dist) 
            break;           
    }
                     
    return d0;     
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (fragCoord-0.5*iResolution.xy)/iResolution.y;
    vec3 col = vec3(0);
    
    // Camera:ro 位置, rd 方向
    vec3 ro = vec3(0,1,0);
    vec3 rd = normalize(vec3(uv.x,uv.y,1.));
    
    float d = RayMarch(ro,rd);
    d/=6.;
    col = vec3(d);

    // Output to screen
    fragColor = vec4(col,1.0);
}

在这里插入图片描述
如图所示,使用返回的距离来描述场景。如果RGBA颜色值是[0,1]区间,距离越远越接近1,则越白;越近越接近0,则越黑。摄像机与球表面最近的点的距离是d=5,现在我们除以一个数,将球显示得更黑,这里我们除以6,即摄像机与球心的距离。

这个时候我们需要加入灯光,计算阴影。灯光照射的方向与顶点的法线的点积是在该点的光照强度的影响值。我们假设灯光的方向是 vec3(0,5,6),现在我们计算顶点的法线;

vec3 lightPos = vec3(0,5,6);

顶点法线的计算

// 顶点的法线
vec3 GetNormal(vec3 p)
{
    float d = GetDist(p);
    vec2 e = vec2(0.01,0);
    
    vec3 n = d-vec3(
        GetDist(p-e.xyy),
        GetDist(p-e.yxy),
        GetDist(p-e.yyx));
    return normalize(n);
}

有了法线,我们就可以计算灯光了

float GetLight(vec3 p)
{
    vec3 lightPos = vec3(0,5,6);
    lightPos.xz += vec2(sin(iTime),cos(iTime))*2.0;
    vec3 l = normalize(lightPos-p);// 点光源
    vec3 n = GetNormal(p);
    
    float dif = clamp(dot(n,l),0.,1.);//漫反射颜色
    
    return dif;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (fragCoord-0.5*iResolution.xy)/iResolution.y;
    vec3 col = vec3(0);
    
    // Camera:ro 位置, rd 方向
    vec3 ro = vec3(0,1,0);
    vec3 rd = normalize(vec3(uv.x,uv.y,1.));
    
    float d = RayMarch(ro,rd);
    
    vec3 p = ro + rd*d;
    
    float dif = GetLight(p);
    col = vec3(dif);
    
    // Output to screen
    fragColor = vec4(col,1.0);
}

但是目前灯光只是在球面上,应该在平面上也投上阴影。

原理:步进下的每一个点出发,沿着灯光方向步进,如果距离小于灯光到点的距离,那么证明碰到了东西。这点点就在阴影下。
在这里插入图片描述

阶段效果

在这里插入图片描述
改良光照计算:

// 计算灯光,点光源
float GetLight(vec3 p)
{
    vec3 lightPos = vec3(0,5,6);
    lightPos.xz += vec2(sin(iTime),cos(iTime))*2.0;
    vec3 l = normalize(lightPos-p);
    vec3 n = GetNormal(p);
    
    float dif = clamp(dot(n,l),0.,1.);
    
    float d = RayMarch(p + n*Surf_Dist*2.0,l);
    if(d<length(lightPos-p))dif*=0.1;
    return dif;
}

完整代码

/********  原理   
/********从摄像机位置向屏幕每一个像素点发射一条光线,光线按照一定步长前进,
/********并检测当前光线是否位于物体表面,据此调整光线前进幅度,直到抵达物体表面,
/********再按照一般光线追踪的方法计算颜色值。*/

// 屏幕左下角UV(0,0) 右上角UV(1,1) 
// 三维坐标:左手坐标系,X轴指向屏幕右侧,Y轴指向屏幕上面,Z轴垂直于屏幕向里


/**
 *常量定义
 */

/**
 *常量定义
 */
//uniform vec3 iResolution;           // 窗口分辨率,单位像素
//uniform float iTime;                // 程序运行的时间,单位秒
//uniform float iTimeDelta;           // 渲染时间,单位秒
//uniform float iFrame;               // 帧率
//uniform vec4 iMouse;                // 鼠标位置
//uniform vec4 iDate;                 // 日期(年,月,日,时)

// 最大步数
// 最大距离
// 最小步进的距离
#define Max_Steps 100
#define Max_Dist 100.
#define Surf_Dist 0.01

//获取距离 
float GetDist(vec3 p)
{
    vec4 s = vec4(0,1,6,1);// 球的位置(s.xyz)和半径(s.w)
    float sphereDist = length(p-s.xyz)-s.w;// P点到球面的距离
    float planeDist  = p.y;// P点到平面的距离
    
    float d = min(sphereDist,planeDist); //球体中心点到平面的最小距离
    return d;
}

//光线步进 
float RayMarch(vec3 ro, vec3 rd)
{
    float d0 = 0.;
    for(int i = 0; i < Max_Steps; i++)
    {
        vec3 p = ro + rd*d0;
        float ds = GetDist(p);
        d0+=ds;
        if(d0>Max_Dist || ds < Surf_Dist) 
            break;           
    }
                     
    return d0;     
}

// 顶点的法线
vec3 GetNormal(vec3 p)
{
    float d = GetDist(p);
    vec2 e = vec2(0.01,0);
    
    vec3 n = d-vec3(
        GetDist(p-e.xyy),
        GetDist(p-e.yxy),
        GetDist(p-e.yyx));
    return normalize(n);
}

// 计算灯光,点光源
float GetLight(vec3 p)
{
    vec3 lightPos = vec3(0,5,6);
    lightPos.xz += vec2(sin(iTime),cos(iTime))*2.0;
    vec3 l = normalize(lightPos-p);
    vec3 n = GetNormal(p);
    
    float dif = clamp(dot(n,l),0.,1.);
    
    float d = RayMarch(p + n*Surf_Dist*2.0,l);
    if(d<length(lightPos-p))dif*=0.1;
    return dif;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (fragCoord-0.5*iResolution.xy)/iResolution.y;
    vec3 col = vec3(0);
    
    // Camera:ro 位置, rd 方向
    vec3 ro = vec3(0,1,0);
    vec3 rd = normalize(vec3(uv.x,uv.y,1.));
    
    float d = RayMarch(ro,rd);
    
    vec3 p = ro + rd*d;
    
    float dif = GetLight(p);
    
    
    //d/=6.0;
    col = vec3(dif);

    // Output to screen
    fragColor = vec4(col,1.0);
}

运行效果

展示

在这里插入图片描述

参考引用机器猫
这里感谢
机器猫

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ShaderToy入门初体验 的相关文章

  • STC15F2K60S2单片机的串口通信程序及相关知识

    一 STC15F2K60S2单片机简介 中国深圳宏晶STC系列单片机是2005年推出中国本土的第一款具有全球竞争力的 且与MCS 51兼容的STC系列单片机 它完全兼容51单片机 是新一代增强型单片机 速度快 抗干扰性强 加密性强 带ADC
  • 解决WIN10 ,在资源管理器搜索文件时,必须按回车才能开始搜索的问题

    解决WIN10 在资源管理器搜索文件时 必须按回车才能开始搜索的问题 在使用WIN10中 在资源管理器中经常搜索文件 而每次搜索文件时 必须按回车才能开始搜索 不像WIN7那个 每输入一个字符 就自动搜索所输入的字符或字条串 如果偶尔用一下
  • 李宏毅对抗生成网络 (GAN)教程(3)Unsupervised Conditional Generation

    文章目录 两大类做法 一 Direct Transformation Cycle GAN Star GAN 二 Projection to Common Space 1 共享参数 2 加一个domain discriminator 3 Cy
  • vue中自定义table的样式不生效

    页面中style带有scoped导致不生效 只能写在全局样式中
  • vector容器

    前言 vector的数据安排以及操作方式 与数组十分相似 他们的唯一差别就是数组是静态空间 而vector是动态空间 当他要去扩容空间的时候 编译器内部会帮助我们去寻找一片新的空间 自动释放以前旧的空间 在vector的实现技术中 关键在于
  • python selenium 用IE下载报 要打开或保存来自 提示问题的一种解决办法

    import win32api import win32con time sleep 2 alt s快捷键 win32api keybd event 0x12 0 0 0 按下alt win32api keybd event 83 0 0
  • 以太坊智能合约字节码深入解析

    智能合约编写好之后需要通过编译器编译后才能在虚拟机上运行 智能合约的编译结果称为字节码 字节码是一串十六进制数字编码的字节数组 字节码的解析是以一个字节为单位 每个字节都表示一个EVM指令或一个操作数据 我们通过一个简单的智能合约来分析智能
  • 结构光的概念及其实现三维成像的主要原理

    结构光三维成像的硬件主要由相机和投射器组成 结构光就是通过投射器投射到被测物体表面的主动结构信息 如激光条纹 格雷码 正弦条纹等 然后 通过单个或多个相机拍摄被测表面即得结构光图像 最后 基于三角测量原理经过图像三维解析计算从而实现三维重建
  • 微特技术钢丝绳探伤仪自动监测系统功能概述

    前言 长期以来 钢丝绳的使用和管理始终是各企业设备管理的难点 因钢丝绳断裂造成的事故屡见不鲜 而每一次事故都伴随着财产损失和人员伤亡等严重后果 截至目前 肉眼看 卡尺量等依然是普遍性的钢丝绳检测手段 检测技术与用户的安全管理需求之间存在着巨
  • 服务器操作系统品牌怎么看,服务器操作系统品牌怎么看

    服务器操作系统品牌怎么看 内容精选 换一换 华为云帮助中心 为用户提供产品简介 价格说明 购买指南 用户指南 API参考 最佳实践 常见问题 视频帮助等技术文档 帮助您快速上手使用华为云服务 以Linux操作系统为例 如果在变更规格后执行i
  • pta 一元多项式的乘法与加法运算

    7 2 一元多项式的乘法与加法运算 20 分 设计函数分别求两个一元多项式的乘积与和 输入格式 输入分2行 每行分别先给出多项式非零项的个数 再以指数递降方式输入一个多项式非零项系数和指数 绝对值均为不超过1000的整数 数字间以空格分隔
  • 关于selenium发起的浏览器被检测的解决方案

    场景 很多时候 我们在使用selenium做模拟爬取的时候 会碰到被检测的情况 其实关于selenium的检测是很好做的 因为selenium生成的浏览器在请求的时候 头部都会带上selenium的特征 这个特征就很好被服务器检测 解决方案
  • 【Django】使用模型对象的一点小坑

    先上结论 当使用manage py inspectdb table name 自动生成models py内容 如果数据库表中列包含大写字母 会被自动转换成小写字母 比如最近在做一个project 使用Django自带的模板对象来插入数据 本

随机推荐

  • XSS漏洞攻击

    目录 前言 Xss简介 XSS攻击原理 Xss攻击种类 反射型XSS get 储存型xss DOM型xss 前言 1 在掌握xss之前 首先要了解什么是html css JavaScript 1 Html 超文本标记语言 是一种用于创建网页
  • What‘s the difference between ‘virtual memory‘ and ‘swap space‘?

    There s an excellent explantation of virtual memory over on superuser Simply put virtual memory is a combination of RAM
  • 设计模式--过滤器模式

    过滤器模式 又叫标准模式属于结构型模式基本原理 创建过滤器 过滤出符合标准的对象 主要流程 1 准备原料 将对象抽象成有共性的一类 2 根据需求的不同创建各种过滤器 3 依靠过滤器过滤出需要的对象注意 过滤器是工具 创建的对象是原料 inc
  • Java版本号处理-split 点号 split(".")

    近期在做一个版本号的判断 在使用 Java split 方法时 希望把版本号中的数字组成数组 很自然的 我用了 split 来分割成数组 结果不行 String v 1 0 1 String vs v split int len vs le
  • 多中机器学习模型对比可视化

    传统的机器学习模型的选择往往是凭借经验和习惯 部分人一般情况会用TPOT今天最佳模型调参 但是也许要对比其他模型在哪些模型衡量指标下的优劣势 这里提供一个简单的效果对比工具 import matplotlib pyplot as plt m
  • 数睿通2.0数据中台数据资产、数据集市功能发布

    引言 数睿通 2 0 数据中台迎来了 7 月份的首次更新 本次更新主要是添加了数据资产 数据集市的相关核心功能 完整的功能实现尚需要时间打磨 因为最近工作实在是有些繁忙 琐事也多 所以能投入开发的时间着实有限 只实现了部分功能 通过数据资产
  • 路由器安装cloudflared进行内网穿透,安全访问内网

    文章目录 前言 一 Cloudflare Argo Tunnel是什么 二 使用步骤 1 开始准备 https developers cloudflare com cloudflare one setup 2 创建隧道 https deve
  • Win10系统下VisualStudio2019编译配置LAStools-v2.0.0

    目录 一 概述 1 LAStools v2 0 0 2 下载解压 二 源码编译 2 1 打开 2 2 移除加载失败的项目 2 3 修改代码 2 4 配置管理器 2 5 修改输出目录 2 6 修改头文件目录 2 7 修改运行库 2 8 生成l
  • matlabplot———散点图

    散点图 散点图 散点图的分类 散点图的优势 绘制散点图 散点图 散点图是指在回归分析中 数据点在直角坐标系平面上的分布图 散点图表示因变量随自变量而变化的大致趋势 在数据分析可视化时 一般会先绘制散点图 初步了解自变量和应变量之间的关系 通
  • wsl2安装archlinux

    前提 开启了WSL 虚拟平台 有WSL2内核 可以开启WSL2 下载Archlinux镜像 镜像名称archlinux bootstrap 2022 01 01 x86 64 tar gz 推荐到清华镜像站下载 下载LxRunOffline
  • 四.在centos上搭建Jira管理工具

    打开虚拟机并点击启动 再打开finalshell双击连接 打开navicat12 方法一 再在下载包里打开 1 下载配置jira 先进入根目录cd 创建两个文件mkdir software mkdir jiradata 进入software
  • jsp 页面编码优先级

    优先级如上图 另外贴一张之前的图
  • Ubantu——Linux文件解压和压缩

    Linux文件压缩和解压指令 一 gzip和gunzip 二 tar命令 三 tar解压和查看 压缩形式介绍 一 gzip和gunzip 例如 二 tar命令 使用区别如下 三 tar解压和查看 tar xvf 加上要解压的文件名字
  • 关于React项目中百度地图API的使用

    一 前言 1 首先进入百度地图API官方地址https lbsyun baidu com 选择适合自己项目的开发文档 我这次使用的是Javascript API 2 点击开发指南 gt Hello World 会有详细步骤介绍使用流程 按步
  • 牛客刷题 数组求和

    在leetcode上刷到第一题 又在牛客上刷到与第一题相似的第二 第三题 整理如下 第一题 给定一个整数数组 nums 和一个目标值 target 请你在该数组中找出和为目标值的那 两个 整数 并返回他们的数组下标 解题思路 public
  • 手机功能测试主要测哪些方面?

    1 RF收发信机指标测试 测试发射功率 发射频谱 接收灵敏度等等 2 音频指标测试 检查或测试发送音频灵敏度 振铃响度 受话器响度 失真度 侧音 免提功能等等 3 LCD和菜单功能的检查 看是否与说明书中所述内容相一致 4 各按键触觉和力度
  • docker基础:把容器做成镜像

    安装 Lua 语言环境 apt get update y apt get install y luajit luarocks 再用 exit 命令退出容器 再运行 docker commit 命令 docker commit a44444a
  • Apache优化

    Apache优化 Apache优化 系统层面优化 内核调试 关闭一些不必要运行的服务 或者删除一些不需要的用户 软件优化 Apache Tomcat Nginx Mysql Apache优化 工作模式 1 worker 单进程多线程 是和并
  • docker安装启动es

    原文链接 docker安装启动es 走看看 docker安装es和kibana docker pull elasticsearch 7 4 2 docker pull kibana 7 4 2 创建映射文件夹 并写入配置 mkdir p k
  • ShaderToy入门初体验

    ShaderToy入门初体验 前言 shadertoy https www shadertoy com 那些大神写的demo简直太震撼了 官方给出的how to 第一个程序 点击上图中的 新建 弹出下图界面 代码详解 void mainIm