add examples shaders_basic_pbr and shaders_hybrid_render
BIN
resources/models/old_car_new.glb
Normal file
BIN
resources/models/plane.glb
Normal file
22
resources/shaders/glsl330/hybrid_raster.fs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#version 330
|
||||
|
||||
// Input vertex attributes (from vertex shader)
|
||||
in vec2 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
|
||||
// Input uniform values
|
||||
uniform sampler2D texture0;
|
||||
uniform vec4 colDiffuse;
|
||||
|
||||
// Output fragment color
|
||||
//out vec4 finalColor;
|
||||
|
||||
// NOTE: Add your custom variables here
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 texelColor = texture(texture0, fragTexCoord);
|
||||
|
||||
gl_FragColor = texelColor*colDiffuse*fragColor;
|
||||
gl_FragDepth = gl_FragCoord.z;
|
||||
}
|
||||
284
resources/shaders/glsl330/hybrid_raymarch.fs
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
# version 330
|
||||
|
||||
// Input vertex attributes (from vertex shader)
|
||||
in vec2 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
|
||||
// Input uniform values
|
||||
uniform sampler2D texture0;
|
||||
uniform vec4 colDiffuse;
|
||||
|
||||
// Custom Input Uniform
|
||||
uniform vec3 camPos;
|
||||
uniform vec3 camDir;
|
||||
uniform vec2 screenCenter;
|
||||
|
||||
#define ZERO 0
|
||||
|
||||
// https://learnopengl.com/Advanced-OpenGL/Depth-testing
|
||||
float CalcDepth(in vec3 rd, in float Idist){
|
||||
float local_z = dot(normalize(camDir),rd)*Idist;
|
||||
return (1.0/(local_z) - 1.0/0.01)/(1.0/1000.0 -1.0/0.01);
|
||||
}
|
||||
|
||||
// https://iquilezles.org/articles/distfunctions/
|
||||
float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w )
|
||||
{
|
||||
p.x = abs(p.x);
|
||||
float l = length(p.xy);
|
||||
p.xy = mat2(-c.x, c.y,
|
||||
c.y, c.x)*p.xy;
|
||||
p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x),
|
||||
(p.x>0.0)?p.y:l );
|
||||
p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0);
|
||||
|
||||
vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z);
|
||||
vec2 d = abs(q) - w;
|
||||
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
|
||||
}
|
||||
|
||||
// r = sphere's radius
|
||||
// h = cutting's plane's position
|
||||
// t = thickness
|
||||
float sdSixWayCutHollowSphere( vec3 p, float r, float h, float t )
|
||||
{
|
||||
// Six way symetry Transformation
|
||||
vec3 ap = abs(p);
|
||||
if(ap.x < max(ap.y, ap.z)){
|
||||
if(ap.y < ap.z) ap.xz = ap.zx;
|
||||
else ap.xy = ap.yx;
|
||||
}
|
||||
|
||||
vec2 q = vec2( length(ap.yz), ap.x );
|
||||
|
||||
float w = sqrt(r*r-h*h);
|
||||
|
||||
return ((h*q.x<w*q.y) ? length(q-vec2(w,h)) :
|
||||
abs(length(q)-r) ) - t;
|
||||
}
|
||||
|
||||
// https://iquilezles.org/articles/boxfunctions
|
||||
vec2 iBox( in vec3 ro, in vec3 rd, in vec3 rad )
|
||||
{
|
||||
vec3 m = 1.0/rd;
|
||||
vec3 n = m*ro;
|
||||
vec3 k = abs(m)*rad;
|
||||
vec3 t1 = -n - k;
|
||||
vec3 t2 = -n + k;
|
||||
return vec2( max( max( t1.x, t1.y ), t1.z ),
|
||||
min( min( t2.x, t2.y ), t2.z ) );
|
||||
}
|
||||
|
||||
vec2 opU( vec2 d1, vec2 d2 )
|
||||
{
|
||||
return (d1.x<d2.x) ? d1 : d2;
|
||||
}
|
||||
|
||||
vec2 map( in vec3 pos ){
|
||||
vec2 res = vec2( sdHorseshoe( pos-vec3(-1.0,0.08, 1.0), vec2(cos(1.3),sin(1.3)), 0.2, 0.3, vec2(0.03,0.5) ), 11.5 ) ;
|
||||
res = opU(res, vec2( sdSixWayCutHollowSphere( pos-vec3(0.0, 1.0, 0.0), 4.0, 3.5, 0.5 ), 4.5 )) ;
|
||||
return res;
|
||||
}
|
||||
|
||||
// https://www.shadertoy.com/view/Xds3zN
|
||||
vec2 raycast( in vec3 ro, in vec3 rd ){
|
||||
vec2 res = vec2(-1.0,-1.0);
|
||||
|
||||
float tmin = 1.0;
|
||||
float tmax = 20.0;
|
||||
|
||||
// raytrace floor plane
|
||||
float tp1 = (-ro.y)/rd.y;
|
||||
if( tp1>0.0 )
|
||||
{
|
||||
tmax = min( tmax, tp1 );
|
||||
res = vec2( tp1, 1.0 );
|
||||
}
|
||||
|
||||
float t = tmin;
|
||||
for( int i=0; i<70 ; i++ )
|
||||
{
|
||||
if(t>tmax) break;
|
||||
vec2 h = map( ro+rd*t );
|
||||
if( abs(h.x)<(0.0001*t) )
|
||||
{
|
||||
res = vec2(t,h.y);
|
||||
break;
|
||||
}
|
||||
t += h.x;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// https://iquilezles.org/articles/rmshadows
|
||||
float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax )
|
||||
{
|
||||
// bounding volume
|
||||
float tp = (0.8-ro.y)/rd.y; if( tp>0.0 ) tmax = min( tmax, tp );
|
||||
|
||||
float res = 1.0;
|
||||
float t = mint;
|
||||
for( int i=ZERO; i<24; i++ )
|
||||
{
|
||||
float h = map( ro + rd*t ).x;
|
||||
float s = clamp(8.0*h/t,0.0,1.0);
|
||||
res = min( res, s );
|
||||
t += clamp( h, 0.01, 0.2 );
|
||||
if( res<0.004 || t>tmax ) break;
|
||||
}
|
||||
res = clamp( res, 0.0, 1.0 );
|
||||
return res*res*(3.0-2.0*res);
|
||||
}
|
||||
|
||||
|
||||
// https://iquilezles.org/articles/normalsSDF
|
||||
vec3 calcNormal( in vec3 pos )
|
||||
{
|
||||
vec2 e = vec2(1.0,-1.0)*0.5773*0.0005;
|
||||
return normalize( e.xyy*map( pos + e.xyy ).x +
|
||||
e.yyx*map( pos + e.yyx ).x +
|
||||
e.yxy*map( pos + e.yxy ).x +
|
||||
e.xxx*map( pos + e.xxx ).x );
|
||||
}
|
||||
|
||||
// https://iquilezles.org/articles/nvscene2008/rwwtt.pdf
|
||||
float calcAO( in vec3 pos, in vec3 nor )
|
||||
{
|
||||
float occ = 0.0;
|
||||
float sca = 1.0;
|
||||
for( int i=ZERO; i<5; i++ )
|
||||
{
|
||||
float h = 0.01 + 0.12*float(i)/4.0;
|
||||
float d = map( pos + h*nor ).x;
|
||||
occ += (h-d)*sca;
|
||||
sca *= 0.95;
|
||||
if( occ>0.35 ) break;
|
||||
}
|
||||
return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y);
|
||||
}
|
||||
|
||||
// https://iquilezles.org/articles/checkerfiltering
|
||||
float checkersGradBox( in vec2 p )
|
||||
{
|
||||
// filter kernel
|
||||
vec2 w = fwidth(p) + 0.001;
|
||||
// analytical integral (box filter)
|
||||
vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w;
|
||||
// xor pattern
|
||||
return 0.5 - 0.5*i.x*i.y;
|
||||
}
|
||||
|
||||
// https://www.shadertoy.com/view/tdS3DG
|
||||
vec4 render( in vec3 ro, in vec3 rd)
|
||||
{
|
||||
// background
|
||||
vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3;
|
||||
|
||||
// raycast scene
|
||||
vec2 res = raycast(ro,rd);
|
||||
float t = res.x;
|
||||
float m = res.y;
|
||||
if( m>-0.5 )
|
||||
{
|
||||
vec3 pos = ro + t*rd;
|
||||
vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos );
|
||||
vec3 ref = reflect( rd, nor );
|
||||
|
||||
// material
|
||||
col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) );
|
||||
float ks = 1.0;
|
||||
|
||||
if( m<1.5 )
|
||||
{
|
||||
float f = checkersGradBox( 3.0*pos.xz);
|
||||
col = 0.15 + f*vec3(0.05);
|
||||
ks = 0.4;
|
||||
}
|
||||
|
||||
// lighting
|
||||
float occ = calcAO( pos, nor );
|
||||
|
||||
vec3 lin = vec3(0.0);
|
||||
|
||||
// sun
|
||||
{
|
||||
vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) );
|
||||
vec3 hal = normalize( lig-rd );
|
||||
float dif = clamp( dot( nor, lig ), 0.0, 1.0 );
|
||||
//if( dif>0.0001 )
|
||||
dif *= calcSoftshadow( pos, lig, 0.02, 2.5 );
|
||||
float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0);
|
||||
spe *= dif;
|
||||
spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0);
|
||||
//spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0);
|
||||
lin += col*2.20*dif*vec3(1.30,1.00,0.70);
|
||||
lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks;
|
||||
}
|
||||
// sky
|
||||
{
|
||||
float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 ));
|
||||
dif *= occ;
|
||||
float spe = smoothstep( -0.2, 0.2, ref.y );
|
||||
spe *= dif;
|
||||
spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 );
|
||||
//if( spe>0.001 )
|
||||
spe *= calcSoftshadow( pos, ref, 0.02, 2.5 );
|
||||
lin += col*0.60*dif*vec3(0.40,0.60,1.15);
|
||||
lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks;
|
||||
}
|
||||
// back
|
||||
{
|
||||
float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
|
||||
dif *= occ;
|
||||
lin += col*0.55*dif*vec3(0.25,0.25,0.25);
|
||||
}
|
||||
// sss
|
||||
{
|
||||
float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0);
|
||||
dif *= occ;
|
||||
lin += col*0.25*dif*vec3(1.00,1.00,1.00);
|
||||
}
|
||||
|
||||
col = lin;
|
||||
|
||||
col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) );
|
||||
}
|
||||
|
||||
return vec4(vec3( clamp(col,0.0,1.0) ),t);
|
||||
}
|
||||
|
||||
vec3 CalcRayDir(vec2 nCoord){
|
||||
vec3 horizontal = normalize(cross(camDir,vec3(.0 , 1.0, .0)));
|
||||
vec3 vertical = normalize(cross(horizontal,camDir));
|
||||
return normalize(camDir + horizontal*nCoord.x + vertical*nCoord.y);
|
||||
}
|
||||
|
||||
mat3 setCamera()
|
||||
{
|
||||
vec3 cw = normalize(camDir);
|
||||
vec3 cp = vec3(0.0, 1.0 ,0.0);
|
||||
vec3 cu = normalize( cross(cw,cp) );
|
||||
vec3 cv = ( cross(cu,cw) );
|
||||
return mat3( cu, cv, cw );
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 nCoord = (gl_FragCoord.xy - screenCenter.xy)/screenCenter.y;
|
||||
mat3 ca = setCamera();
|
||||
|
||||
// focal length
|
||||
float fl = length(camDir);
|
||||
vec3 rd = ca * normalize( vec3(nCoord,fl) );
|
||||
vec3 color = vec3(nCoord/2.0 + 0.5, 0.0);
|
||||
float depth = gl_FragCoord.z;
|
||||
{
|
||||
vec4 res = render( camPos - vec3(0.0, 0.0, 0.0) , rd );
|
||||
color = res.xyz;
|
||||
depth = CalcDepth(rd,res.w);
|
||||
}
|
||||
gl_FragColor = vec4(color , 1.0);
|
||||
gl_FragDepth = depth;
|
||||
}
|
||||
162
resources/shaders/glsl330/pbr.fs
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#version 330
|
||||
|
||||
#define MAX_LIGHTS 4
|
||||
#define LIGHT_DIRECTIONAL 0
|
||||
#define LIGHT_POINT 1
|
||||
#define PI 3.14159265358979323846
|
||||
|
||||
struct Light {
|
||||
int enabled;
|
||||
int type;
|
||||
vec3 position;
|
||||
vec3 target;
|
||||
vec4 color;
|
||||
float intensity;
|
||||
};
|
||||
|
||||
// Input vertex attributes (from vertex shader)
|
||||
in vec3 fragPosition;
|
||||
in vec2 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
in vec3 fragNormal;
|
||||
in vec4 shadowPos;
|
||||
in mat3 TBN;
|
||||
|
||||
// Output fragment color
|
||||
out vec4 finalColor;
|
||||
|
||||
// Input uniform values
|
||||
uniform int numOfLights;
|
||||
uniform sampler2D albedoMap;
|
||||
uniform sampler2D mraMap;
|
||||
uniform sampler2D normalMap;
|
||||
uniform sampler2D emissiveMap; // r: Hight g:emissive
|
||||
|
||||
uniform vec2 tiling;
|
||||
uniform vec2 offset;
|
||||
|
||||
uniform int useTexAlbedo;
|
||||
uniform int useTexNormal;
|
||||
uniform int useTexMRA;
|
||||
uniform int useTexEmissive;
|
||||
|
||||
uniform vec4 albedoColor;
|
||||
uniform vec4 emissiveColor;
|
||||
uniform float normalValue;
|
||||
uniform float metallicValue;
|
||||
uniform float roughnessValue;
|
||||
uniform float aoValue;
|
||||
uniform float emissivePower;
|
||||
|
||||
// Input lighting values
|
||||
uniform Light lights[MAX_LIGHTS];
|
||||
uniform vec3 viewPos;
|
||||
|
||||
uniform vec3 ambientColor;
|
||||
uniform float ambient;
|
||||
|
||||
// Reflectivity in range 0.0 to 1.0
|
||||
// NOTE: Reflectivity is increased when surface view at larger angle
|
||||
vec3 SchlickFresnel(float hDotV,vec3 refl)
|
||||
{
|
||||
return refl + (1.0 - refl)*pow(1.0 - hDotV, 5.0);
|
||||
}
|
||||
|
||||
float GgxDistribution(float nDotH,float roughness)
|
||||
{
|
||||
float a = roughness*roughness*roughness*roughness;
|
||||
float d = nDotH*nDotH*(a - 1.0) + 1.0;
|
||||
d = PI*d*d;
|
||||
return (a/max(d,0.0000001));
|
||||
}
|
||||
|
||||
float GeomSmith(float nDotV,float nDotL,float roughness)
|
||||
{
|
||||
float r = roughness + 1.0;
|
||||
float k = r*r/8.0;
|
||||
float ik = 1.0 - k;
|
||||
float ggx1 = nDotV/(nDotV*ik + k);
|
||||
float ggx2 = nDotL/(nDotL*ik + k);
|
||||
return ggx1*ggx2;
|
||||
}
|
||||
|
||||
vec3 ComputePBR()
|
||||
{
|
||||
vec3 albedo = texture(albedoMap,vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb;
|
||||
albedo = vec3(albedoColor.x*albedo.x, albedoColor.y*albedo.y, albedoColor.z*albedo.z);
|
||||
|
||||
float metallic = clamp(metallicValue, 0.0, 1.0);
|
||||
float roughness = clamp(roughnessValue, 0.0, 1.0);
|
||||
float ao = clamp(aoValue, 0.0, 1.0);
|
||||
|
||||
if (useTexMRA == 1)
|
||||
{
|
||||
vec4 mra = texture(mraMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y));
|
||||
metallic = clamp(mra.r + metallicValue, 0.04, 1.0);
|
||||
roughness = clamp(mra.g + roughnessValue, 0.04, 1.0);
|
||||
ao = (mra.b + aoValue)*0.5;
|
||||
}
|
||||
|
||||
vec3 N = normalize(fragNormal);
|
||||
if (useTexNormal == 1)
|
||||
{
|
||||
N = texture(normalMap, vec2(fragTexCoord.x*tiling.x + offset.y, fragTexCoord.y*tiling.y + offset.y)).rgb;
|
||||
N = normalize(N*2.0 - 1.0);
|
||||
N = normalize(N*TBN);
|
||||
}
|
||||
|
||||
vec3 V = normalize(viewPos - fragPosition);
|
||||
|
||||
vec3 emissive = vec3(0);
|
||||
emissive = (texture(emissiveMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb).g*emissiveColor.rgb*emissivePower*useTexEmissive;
|
||||
|
||||
// return N;//vec3(metallic,metallic,metallic);
|
||||
// If dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity
|
||||
vec3 baseRefl = mix(vec3(0.04), albedo.rgb, metallic);
|
||||
vec3 lightAccum = vec3(0.0); // Acumulate lighting lum
|
||||
|
||||
for (int i = 0; i < numOfLights; i++)
|
||||
{
|
||||
vec3 L = normalize(lights[i].position - fragPosition); // Compute light vector
|
||||
vec3 H = normalize(V + L); // Compute halfway bisecting vector
|
||||
float dist = length(lights[i].position - fragPosition); // Compute distance to light
|
||||
float attenuation = 1.0/(dist*dist*0.23); // Compute attenuation
|
||||
vec3 radiance = lights[i].color.rgb*lights[i].intensity*attenuation; // Compute input radiance, light energy comming in
|
||||
|
||||
// Cook-Torrance BRDF distribution function
|
||||
float nDotV = max(dot(N,V), 0.0000001);
|
||||
float nDotL = max(dot(N,L), 0.0000001);
|
||||
float hDotV = max(dot(H,V), 0.0);
|
||||
float nDotH = max(dot(N,H), 0.0);
|
||||
float D = GgxDistribution(nDotH, roughness); // Larger the more micro-facets aligned to H
|
||||
float G = GeomSmith(nDotV, nDotL, roughness); // Smaller the more micro-facets shadow
|
||||
vec3 F = SchlickFresnel(hDotV, baseRefl); // Fresnel proportion of specular reflectance
|
||||
|
||||
vec3 spec = (D*G*F)/(4.0*nDotV*nDotL);
|
||||
|
||||
// Difuse and spec light can't be above 1.0
|
||||
// kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent
|
||||
vec3 kD = vec3(1.0) - F;
|
||||
|
||||
// Mult kD by the inverse of metallnes, only non-metals should have diffuse light
|
||||
kD *= 1.0 - metallic;
|
||||
lightAccum += ((kD*albedo.rgb/PI + spec)*radiance*nDotL)*lights[i].enabled; // Angle of light has impact on result
|
||||
}
|
||||
|
||||
vec3 ambientFinal = (ambientColor + albedo)*ambient*0.5;
|
||||
|
||||
return (ambientFinal + lightAccum*ao + emissive);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 color = ComputePBR();
|
||||
|
||||
// HDR tonemapping
|
||||
color = pow(color, color + vec3(1.0));
|
||||
|
||||
// Gamma correction
|
||||
color = pow(color, vec3(1.0/2.2));
|
||||
|
||||
finalColor = vec4(color, 1.0);
|
||||
}
|
||||
48
resources/shaders/glsl330/pbr.vs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#version 330
|
||||
|
||||
// Input vertex attributes
|
||||
in vec3 vertexPosition;
|
||||
in vec2 vertexTexCoord;
|
||||
in vec3 vertexNormal;
|
||||
in vec4 vertexTangent;
|
||||
in vec4 vertexColor;
|
||||
|
||||
// Input uniform values
|
||||
uniform mat4 mvp;
|
||||
uniform mat4 matModel;
|
||||
uniform mat4 matNormal;
|
||||
uniform vec3 lightPos;
|
||||
uniform vec4 difColor;
|
||||
|
||||
// Output vertex attributes (to fragment shader)
|
||||
out vec3 fragPosition;
|
||||
out vec2 fragTexCoord;
|
||||
out vec4 fragColor;
|
||||
out vec3 fragNormal;
|
||||
out mat3 TBN;
|
||||
|
||||
const float normalOffset = 0.1;
|
||||
|
||||
void main()
|
||||
{
|
||||
// Compute binormal from vertex normal and tangent
|
||||
vec3 vertexBinormal = cross(vertexNormal, vertexTangent.xyz) * vertexTangent.w;
|
||||
|
||||
// Compute fragment normal based on normal transformations
|
||||
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
|
||||
|
||||
// Compute fragment position based on model transformations
|
||||
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
|
||||
|
||||
fragTexCoord = vertexTexCoord*2.0;
|
||||
fragNormal = normalize(normalMatrix*vertexNormal);
|
||||
vec3 fragTangent = normalize(normalMatrix*vertexTangent.xyz);
|
||||
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
|
||||
vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
|
||||
fragBinormal = cross(fragNormal, fragTangent);
|
||||
|
||||
TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
|
||||
|
||||
// Calculate final vertex position
|
||||
gl_Position = mvp*vec4(vertexPosition, 1.0);
|
||||
}
|
||||
BIN
resources/textures/old_car_d.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
resources/textures/old_car_e.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/textures/old_car_mra.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/textures/old_car_n.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
resources/textures/road_a.png
Normal file
|
After Width: | Height: | Size: 623 KiB |
BIN
resources/textures/road_mra.png
Normal file
|
After Width: | Height: | Size: 657 KiB |
BIN
resources/textures/road_n.png
Normal file
|
After Width: | Height: | Size: 645 KiB |