diff --git a/Shaders/deferred_light/deferred_light.frag.glsl b/Shaders/deferred_light/deferred_light.frag.glsl index 4e4f6efb..c6cd9f72 100644 --- a/Shaders/deferred_light/deferred_light.frag.glsl +++ b/Shaders/deferred_light/deferred_light.frag.glsl @@ -106,16 +106,10 @@ uniform vec2 cameraPlane; #ifdef _ShadowMap //!uniform vec2 lightProj; - // uniform samplerCubeShadow shadowMap0; //arm_dev - //!uniform samplerCube shadowMap0; - //!uniform samplerCube shadowMap1; - //!uniform samplerCube shadowMap2; - //!uniform samplerCube shadowMap3; + // uniform samplerCubeShadow shadowMapPoint[4]; //arm_dev + //!uniform samplerCube shadowMapPoint[4]; #ifdef _Spot - //!uniform sampler2D shadowMapSpot0; - //!uniform sampler2D shadowMapSpot1; - //!uniform sampler2D shadowMapSpot2; - //!uniform sampler2D shadowMapSpot3; + //!uniform sampler2D shadowMapSpot[4]; //!uniform mat4 LWVPSpot0; //!uniform mat4 LWVPSpot1; //!uniform mat4 LWVPSpot2; diff --git a/Shaders/deferred_light/deferred_light.json b/Shaders/deferred_light/deferred_light.json index a8a5e4f3..07905e5d 100755 --- a/Shaders/deferred_light/deferred_light.json +++ b/Shaders/deferred_light/deferred_light.json @@ -197,6 +197,26 @@ "name": "spotData", "link": "_spotData", "ifdef": ["_SinglePoint", "_Spot"] + }, + { + "name": "LWVPSpot0", + "link": "_biasLightWorldViewProjectionMatrixSpot0", + "ifdef": ["_Spot", "_ShadowMap"] + }, + { + "name": "LWVPSpot1", + "link": "_biasLightWorldViewProjectionMatrixSpot1", + "ifdef": ["_Spot", "_ShadowMap"] + }, + { + "name": "LWVPSpot2", + "link": "_biasLightWorldViewProjectionMatrixSpot2", + "ifdef": ["_Spot", "_ShadowMap"] + }, + { + "name": "LWVPSpot3", + "link": "_biasLightWorldViewProjectionMatrixSpot3", + "ifdef": ["_Spot", "_ShadowMap"] } ], "vertex_shader": "../include/pass_viewray.vert.glsl", diff --git a/Shaders/deferred_light_mobile/deferred_light.frag.glsl b/Shaders/deferred_light_mobile/deferred_light.frag.glsl index 6acd0857..8ab3e00c 100644 --- a/Shaders/deferred_light_mobile/deferred_light.frag.glsl +++ b/Shaders/deferred_light_mobile/deferred_light.frag.glsl @@ -45,16 +45,10 @@ uniform vec2 cameraPlane; #ifdef _ShadowMap //!uniform vec2 lightProj; - // uniform samplerCubeShadow shadowMap0; //arm_dev - //!uniform samplerCube shadowMap0; - //!uniform samplerCube shadowMap1; - //!uniform samplerCube shadowMap2; - //!uniform samplerCube shadowMap3; + // uniform samplerCubeShadow shadowMapPoint[4]; //arm_dev + //!uniform samplerCube shadowMapPoint[4]; #ifdef _Spot - //!uniform sampler2D shadowMapSpot0; - //!uniform sampler2D shadowMapSpot1; - //!uniform sampler2D shadowMapSpot2; - //!uniform sampler2D shadowMapSpot3; + //!uniform sampler2D shadowMapSpot[4]; //!uniform mat4 LWVPSpot0; //!uniform mat4 LWVPSpot1; //!uniform mat4 LWVPSpot2; diff --git a/Shaders/deferred_light_mobile/deferred_light_mobile.json b/Shaders/deferred_light_mobile/deferred_light_mobile.json index 46dd0f08..f08b10ba 100644 --- a/Shaders/deferred_light_mobile/deferred_light_mobile.json +++ b/Shaders/deferred_light_mobile/deferred_light_mobile.json @@ -136,6 +136,26 @@ "name": "spotData", "link": "_spotData", "ifdef": ["_SinglePoint", "_Spot"] + }, + { + "name": "LWVPSpot0", + "link": "_biasLightWorldViewProjectionMatrixSpot0", + "ifdef": ["_Spot", "_ShadowMap"] + }, + { + "name": "LWVPSpot1", + "link": "_biasLightWorldViewProjectionMatrixSpot1", + "ifdef": ["_Spot", "_ShadowMap"] + }, + { + "name": "LWVPSpot2", + "link": "_biasLightWorldViewProjectionMatrixSpot2", + "ifdef": ["_Spot", "_ShadowMap"] + }, + { + "name": "LWVPSpot3", + "link": "_biasLightWorldViewProjectionMatrixSpot3", + "ifdef": ["_Spot", "_ShadowMap"] } ], "vertex_shader": "../include/pass_viewray.vert.glsl", diff --git a/Shaders/std/clusters.glsl b/Shaders/std/clusters.glsl index 9c9786b4..c9af62bf 100644 --- a/Shaders/std/clusters.glsl +++ b/Shaders/std/clusters.glsl @@ -1,6 +1,6 @@ const int maxLights = 16; -const int maxLightsCluster = 8; +const int maxLightsCluster = 4; // Ensure fast loop unroll before going higher const float clusterNear = 3.0; const vec3 clusterSlices = vec3(16, 16, 16); diff --git a/Shaders/std/light.glsl b/Shaders/std/light.glsl index 6fdb7f4f..9f65d751 100644 --- a/Shaders/std/light.glsl +++ b/Shaders/std/light.glsl @@ -10,16 +10,10 @@ #ifdef _ShadowMap uniform vec2 lightProj; - // uniform samplerCubeShadow shadowMap0; //arm_dev - uniform samplerCube shadowMap0; - uniform samplerCube shadowMap1; - uniform samplerCube shadowMap2; - uniform samplerCube shadowMap3; + // uniform samplerCubeShadow shadowMapPoint[4]; //arm_dev + uniform samplerCube shadowMapPoint[4]; #ifdef _Spot - uniform sampler2D shadowMapSpot0; - uniform sampler2D shadowMapSpot1; - uniform sampler2D shadowMapSpot2; - uniform sampler2D shadowMapSpot3; + uniform sampler2D shadowMapSpot[4]; uniform mat4 LWVPSpot0; uniform mat4 LWVPSpot1; uniform mat4 LWVPSpot2; @@ -57,8 +51,22 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co direct *= smoothstep(spotB, spotA, spotEffect); } #ifdef _ShadowMap - vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0); - if (lPos.w > 0.0) direct *= shadowTest(shadowMapSpot0, lPos.xyz / lPos.w, bias, shadowmapSize); + if (index == 0) { + vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias, shadowmapSize); + } + else if (index == 1) { + vec4 lPos = LWVPSpot1 * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias, shadowmapSize); + } + else if (index == 2) { + vec4 lPos = LWVPSpot2 * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias, shadowmapSize); + } + else if (index == 3) { + vec4 lPos = LWVPSpot3 * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias, shadowmapSize); + } #endif return direct; } @@ -88,10 +96,10 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #ifdef _ShadowMap // Oh well.. - if (index == 0) direct *= PCFCube(shadowMap0, ld, -l, bias, lightProj, n); - else if (index == 1) direct *= PCFCube(shadowMap1, ld, -l, bias, lightProj, n); - else if (index == 2) direct *= PCFCube(shadowMap2, ld, -l, bias, lightProj, n); - else if (index == 3) direct *= PCFCube(shadowMap3, ld, -l, bias, lightProj, n); + if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n); + else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n); + else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n); + else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n); #endif return direct; diff --git a/Shaders/std/light_mobile.glsl b/Shaders/std/light_mobile.glsl index 3490b9a8..5b056830 100644 --- a/Shaders/std/light_mobile.glsl +++ b/Shaders/std/light_mobile.glsl @@ -9,16 +9,10 @@ #ifdef _ShadowMap uniform vec2 lightProj; - // uniform samplerCubeShadow shadowMap0; //arm_dev - uniform samplerCube shadowMap0; - uniform samplerCube shadowMap1; - uniform samplerCube shadowMap2; - uniform samplerCube shadowMap3; + // uniform samplerCubeShadow shadowMapPoint[4]; //arm_dev + uniform samplerCube shadowMapPoint[4]; #ifdef _Spot - uniform sampler2D shadowMapSpot0; - uniform sampler2D shadowMapSpot1; - uniform sampler2D shadowMapSpot2; - uniform sampler2D shadowMapSpot3; + uniform sampler2D shadowMapSpot[4]; uniform mat4 LWVPSpot0; uniform mat4 LWVPSpot1; uniform mat4 LWVPSpot2; @@ -56,9 +50,21 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co direct *= smoothstep(spotB, spotA, spotEffect); } #ifdef _ShadowMap - vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0); - if (lPos.w > 0.0) { - direct *= shadowTest(shadowMapSpot0, lPos.xyz / lPos.w, bias, shadowmapSize); + if (index == 0) { + vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias, shadowmapSize); + } + else if (index == 1) { + vec4 lPos = LWVPSpot1 * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias, shadowmapSize); + } + else if (index == 2) { + vec4 lPos = LWVPSpot2 * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias, shadowmapSize); + } + else if (index == 3) { + vec4 lPos = LWVPSpot3 * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias, shadowmapSize); } #endif return direct; @@ -66,10 +72,10 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #endif #ifdef _ShadowMap - if (index == 0) direct *= PCFCube(shadowMap0, ld, -l, bias, lightProj, n); - else if (index == 1) direct *= PCFCube(shadowMap1, ld, -l, bias, lightProj, n); - else if (index == 2) direct *= PCFCube(shadowMap2, ld, -l, bias, lightProj, n); - else if (index == 3) direct *= PCFCube(shadowMap3, ld, -l, bias, lightProj, n); + if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n); + else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n); + else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n); + else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n); #endif return direct; diff --git a/Sources/armory/renderpath/Inc.hx b/Sources/armory/renderpath/Inc.hx index 6c373892..09e744f9 100644 --- a/Sources/armory/renderpath/Inc.hx +++ b/Sources/armory/renderpath/Inc.hx @@ -57,35 +57,19 @@ class Inc { break; } for (i in 0...pointIndex) { - var n = "shadowMap" + i; + var n = "shadowMapPoint[" + i + "]"; path.bindTarget(n, n); } for (i in 0...spotIndex) { - var n = "shadowMapSpot" + i; + var n = "shadowMapSpot[" + i + "]"; path.bindTarget(n, n); } - - // var target = shadowMapName(l); - // if (target == "shadowMapCube") { - // #if kha_webgl - // // Bind empty map to non-cubemap sampler - // path.bindTarget("arm_empty", "shadowMap"); - // #end - // path.bindTarget("shadowMapCube", "shadowMapCube"); - // } - // else { - // #if kha_webgl - // // Bind empty map to cubemap sampler - // path.bindTarget("arm_empty_cube", "shadowMapCube"); - // #end - // path.bindTarget("shadowMap", "shadowMap"); - // } } static function shadowMapName(l:iron.object.LightObject):String { if (l.data.raw.type == "sun") return "shadowMap"; - if (l.data.raw.type == "point") return "shadowMap" + pointIndex; - else return "shadowMapSpot" + spotIndex; + if (l.data.raw.type == "point") return "shadowMapPoint[" + pointIndex + "]"; + else return "shadowMapSpot[" + spotIndex + "]"; } static function getShadowMap(l:iron.object.LightObject):String { @@ -108,7 +92,9 @@ class Inc { var sizew = path.light.data.raw.shadowmap_size; var sizeh = sizew; #if arm_csm // Cascades - atlas on x axis - sizew = sizew * iron.object.LightObject.cascadeCount; + if (l.data.raw.type == "sun") { + sizew = sizew * iron.object.LightObject.cascadeCount; + } #end var t = new RenderTargetRaw(); t.name = target; @@ -129,7 +115,6 @@ class Inc { for (l in iron.Scene.active.lights) { if (!l.visible || !l.data.raw.cast_shadow) continue; path.light = l; - var shadowmap = Inc.getShadowMap(l); var faces = l.data.raw.shadowmap_cube ? 6 : 1; for (i in 0...faces) { diff --git a/blender/arm/lib/make_datas.py b/blender/arm/lib/make_datas.py index d16ea7f5..f092c541 100644 --- a/blender/arm/lib/make_datas.py +++ b/blender/arm/lib/make_datas.py @@ -148,44 +148,50 @@ def parse_shader(sres, c, con, defs, lines, parse_attributes): found = True break if found == False: - tu = {} - tu['name'] = cid - # sampler2D / image2D - if ctype.startswith('image') or ctype.startswith('uimage'): - tu['is_image'] = True - # Check for link - for l in c['links']: - if l['name'] == cid: - valid_link = True + if cid[-1] == ']': # Array of samplers - sampler2D mySamplers[2] + # Add individual units - mySamplers[0], mySamplers[1] + for i in range(int(cid[-2])): + tu = {} + con['texture_units'].append(tu) + tu['name'] = cid[:-2] + str(i) + ']' + else: + tu = {} + con['texture_units'].append(tu) + tu['name'] = cid + if ctype.startswith('image') or ctype.startswith('uimage'): + tu['is_image'] = True + # Check for link + for l in c['links']: + if l['name'] == cid: + valid_link = True - if 'ifdef' in l: - def_found = False - for d in defs: - for link_def in l['ifdef']: - if d == link_def: - def_found = True + if 'ifdef' in l: + def_found = False + for d in defs: + for link_def in l['ifdef']: + if d == link_def: + def_found = True + break + if def_found: + break + if not def_found: + valid_link = False + + if 'ifndef' in l: + def_found = False + for d in defs: + for link_def in l['ifndef']: + if d == link_def: + def_found = True + break + if def_found: break if def_found: - break - if not def_found: - valid_link = False + valid_link = False - if 'ifndef' in l: - def_found = False - for d in defs: - for link_def in l['ifndef']: - if d == link_def: - def_found = True - break - if def_found: - break - if def_found: - valid_link = False - - if valid_link: - tu['link'] = l['link'] - break - con['texture_units'].append(tu) + if valid_link: + tu['link'] = l['link'] + break else: # Constant if cid.find('[') != -1: # Float arrays cid = cid.split('[')[0] @@ -196,6 +202,7 @@ def parse_shader(sres, c, con, defs, lines, parse_attributes): break if found == False: const = {} + con['constants'].append(const) const['type'] = ctype const['name'] = cid # Check for link @@ -230,7 +237,6 @@ def parse_shader(sres, c, con, defs, lines, parse_attributes): if valid_link: const['link'] = l['link'] break - con['constants'].append(const) def make(res, base_name, json_data, fp, defs): sres = {} diff --git a/blender/arm/material/make_cluster.py b/blender/arm/material/make_cluster.py index fa2c9ba9..32b51c96 100644 --- a/blender/arm/material/make_cluster.py +++ b/blender/arm/material/make_cluster.py @@ -11,10 +11,7 @@ def write(vert, frag): frag.add_uniform('sampler2D clustersData', link='_clustersData') if is_shadows: frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True) - frag.add_uniform('samplerCube shadowMap0', included=True) - frag.add_uniform('samplerCube shadowMap1', included=True) - frag.add_uniform('samplerCube shadowMap2', included=True) - frag.add_uniform('samplerCube shadowMap3', included=True) + frag.add_uniform('samplerCube shadowMapPoint[4]', included=True) vert.add_out('vec4 wvpposition') vert.write('wvpposition = gl_Position;') # wvpposition.z / wvpposition.w @@ -27,8 +24,15 @@ def write(vert, frag): frag.write('#endif') if '_Spot' in wrd.world_defs: + frag.add_uniform('vec4 lightsArraySpot[maxLights]', link='_lightsArraySpot') frag.write('int numSpots = int(texelFetch(clustersData, ivec2(clusterI, 1 + maxLightsCluster), 0).r * 255);') frag.write('int numPoints = numLights - numSpots;') + if is_shadows: + frag.add_uniform('mat4 LWVPSpot0', link='_biasLightWorldViewProjectionMatrixSpot0', included=True) + frag.add_uniform('mat4 LWVPSpot1', link='_biasLightWorldViewProjectionMatrixSpot1', included=True) + frag.add_uniform('mat4 LWVPSpot2', link='_biasLightWorldViewProjectionMatrixSpot2', included=True) + frag.add_uniform('mat4 LWVPSpot3', link='_biasLightWorldViewProjectionMatrixSpot3', included=True) + frag.add_uniform('sampler2D shadowMapSpot[4]', included=True) frag.write('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {') frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);') diff --git a/blender/arm/material/make_mesh.py b/blender/arm/material/make_mesh.py index 7dc4c36f..76844b97 100644 --- a/blender/arm/material/make_mesh.py +++ b/blender/arm/material/make_mesh.py @@ -512,19 +512,28 @@ def make_forward_mobile(con_mesh): frag.write('vec3 ld = pointPos - wposition;') frag.write('vec3 l = normalize(ld);') frag.write('float dotNL = max(dot(n, l), 0.0);') - # if '_Spot' in wrd.world_defs: - # frag.add_uniform('vec3 spotDir', link='_spotDirection') - # frag.add_uniform('vec2 spotData', link='_spotData') - # if is_shadows: - # frag.add_uniform('sampler2D shadowMapSpot0', included=True) - if is_shadows: + if '_Spot' in wrd.world_defs: + frag.add_uniform('vec3 spotDir', link='_spotDirection') + frag.add_uniform('vec2 spotData', link='_spotData') + if is_shadows: + vert.add_out('vec4 spotPosition') + vert.add_uniform('mat4 LWVPSpot0', '_biasLightWorldViewProjectionMatrixSpot0') + vert.write('spotPosition = LWVPSpot0 * spos;') + frag.add_uniform('float pointBias', link='_pointShadowsBias') + frag.add_uniform('sampler2D shadowMapSpot[1]') + frag.write('if (spotPosition.w > 0.0) {') + frag.write(' vec3 lPos = spotPosition.xyz / spotPosition.w;') + frag.write(' const float texelSize = 1.0 / shadowmapSize.x;') + frag.write(' visibility = float(texture(shadowMap, lPos.xy).r + pointBias > lPos.z);') + frag.write('}') + elif is_shadows: frag.add_include('std/shadows.glsl') frag.add_uniform('vec2 lightProj', link='_lightPlaneProj') - frag.add_uniform('samplerCube shadowMap0') + frag.add_uniform('samplerCube shadowMapPoint[1]') frag.add_uniform('float pointBias', link='_pointShadowsBias') frag.write('const float s = shadowmapCubePcfSize;') # TODO: incorrect... frag.write('float compare = lpToDepth(ld - n * pointBias * 80, lightProj);') - frag.write('visibility = step(compare, texture(shadowMap0, -l + n * pointBias * 80).r);') + frag.write('visibility = step(compare, texture(shadowMapPoint[0], -l + n * pointBias * 80).r);') frag.write('direct += basecol * dotNL * pointCol * attenuate(distance(wposition, pointPos)) * visibility;') @@ -724,13 +733,17 @@ def make_forward_base(con_mesh, parse_opacity=False): frag.add_uniform('vec3 pointCol', link='_pointColor') if is_shadows: frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True) - frag.add_uniform('samplerCube shadowMap0', included=True) + frag.add_uniform('samplerCube shadowMapPoint[1]', included=True) frag.add_uniform('float pointBias', link='_pointShadowsBias') if '_Spot' in wrd.world_defs: frag.add_uniform('vec3 spotDir', link='_spotDirection') frag.add_uniform('vec2 spotData', link='_spotData') if is_shadows: - frag.add_uniform('sampler2D shadowMapSpot0', included=True) + frag.add_uniform('mat4 LWVPSpot0', '_biasLightWorldViewProjectionMatrixSpot0', included=True) + frag.add_uniform('mat4 LWVPSpot1', '_biasLightWorldViewProjectionMatrixSpot1', included=True) + frag.add_uniform('mat4 LWVPSpot2', '_biasLightWorldViewProjectionMatrixSpot2', included=True) + frag.add_uniform('mat4 LWVPSpot3', '_biasLightWorldViewProjectionMatrixSpot3', included=True) + frag.add_uniform('sampler2D shadowMapSpot[4]', included=True) frag.write('direct += sampleLight(') frag.write(' wposition, n, vVec, dotNV, pointPos, pointCol, albedo, roughness, specular, f0') if is_shadows: @@ -782,7 +795,6 @@ def make_forward_base(con_mesh, parse_opacity=False): frag.write('vec3 voxpos = wposition / voxelgiHalfExtents;') if '_VoxelAO' in wrd.world_defs: frag.write('indirect *= vec3(1.0 - traceAO(voxpos, n, voxels));') - # frag.write('indirect = vec3(1.0 - traceAO(voxpos, n, voxels));') # AO view else: frag.write('vec4 indirectDiffuse = traceDiffuse(voxpos, n, voxels);') frag.write('indirect = indirect * voxelgiEnv + vec3(indirectDiffuse.rgb * voxelgiDiff * basecol);') diff --git a/blender/arm/material/shader.py b/blender/arm/material/shader.py index 98df9ddb..27091e01 100644 --- a/blender/arm/material/shader.py +++ b/blender/arm/material/shader.py @@ -199,7 +199,13 @@ class Shader: uname = ar[-1] if utype.startswith('sampler') or utype.startswith('image') or utype.startswith('uimage'): is_image = True if (utype.startswith('image') or utype.startswith('uimage')) else None - self.context.add_texture_unit(utype, uname, link=link, is_image=is_image) + if uname[-1] == ']': # Array of samplers - sampler2D mySamplers[2] + # Add individual units - mySamplers[0], mySamplers[1] + for i in range(int(uname[-2])): + uname_array = uname[:-2] + str(i) + ']' + self.context.add_texture_unit(utype, uname_array, link=link, is_image=is_image) + else: + self.context.add_texture_unit(utype, uname, link=link, is_image=is_image) else: # Prefer vec4[] for d3d to avoid padding if ar[0] == 'float' and '[' in ar[1]: